first overlay render
This commit is contained in:
parent
ddd692bcbd
commit
7a15fa556b
|
@ -1,129 +0,0 @@
|
|||
/*
|
||||
* Settings of minimap / overlay
|
||||
*/
|
||||
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import { useSelector, shallowEqual } from 'react-redux';
|
||||
import { t } from 'ttag';
|
||||
|
||||
import { coordsFromUrl } from '../core/utils';
|
||||
import templateLoader from '../ui/templateLoader';
|
||||
|
||||
const AddTemplate = ({ close, triggerClose: refClose }) => {
|
||||
const [render, setRender] = useState(false);
|
||||
const [coords, setCoords] = useState(null);
|
||||
const [dimensions, setDimensions] = useState(null);
|
||||
const [title, setTitle] = useState('');
|
||||
const [file, setFile] = useState(null);
|
||||
const imgRef = useRef();
|
||||
const [
|
||||
canvasId,
|
||||
canvases,
|
||||
] = useSelector((state) => [
|
||||
state.canvas.canvasId,
|
||||
state.canvas.canvases,
|
||||
], shallowEqual);
|
||||
const [selectedCanvas, selectCanvas] = useState(canvasId);
|
||||
|
||||
useEffect(() => {
|
||||
window.setTimeout(() => setRender(true), 10);
|
||||
refClose.current = () => setRender(false);
|
||||
}, [refClose]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!file || !imgRef.current) {
|
||||
return;
|
||||
}
|
||||
const fr = new FileReader();
|
||||
fr.onload = () => { imgRef.current.src = fr.result; };
|
||||
fr.readAsDataURL(file);
|
||||
}, [file]);
|
||||
|
||||
const canSubmit = (imgRef.current && file && coords && title && dimensions);
|
||||
|
||||
return (
|
||||
<div
|
||||
className="inarea"
|
||||
style={{
|
||||
opacity: render ? 1 : 0,
|
||||
transition: 'opacity 200ms',
|
||||
}}
|
||||
onTransitionEnd={() => !render && close()}
|
||||
>
|
||||
<form
|
||||
onSubmit={async (event) => {
|
||||
event.preventDefault();
|
||||
if (canSubmit) {
|
||||
await templateLoader.addFile(
|
||||
file, title, selectedCanvas, ...coords, imgRef.current,
|
||||
);
|
||||
setRender(false);
|
||||
}
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src="./logo.svg"
|
||||
alt="preview"
|
||||
key="logo"
|
||||
style={{
|
||||
maxWidth: 96,
|
||||
maxHeight: 96,
|
||||
}}
|
||||
onLoad={(evt) => setDimensions([
|
||||
evt.target.naturalWidth,
|
||||
evt.target.naturalHeight,
|
||||
])}
|
||||
onError={() => setDimensions(null)}
|
||||
ref={imgRef}
|
||||
/>
|
||||
<input
|
||||
type="file"
|
||||
onChange={(evt) => {
|
||||
setDimensions(null);
|
||||
setFile(evt.target.files?.[0]);
|
||||
}}
|
||||
/>
|
||||
<input
|
||||
value={title}
|
||||
type="text"
|
||||
onChange={(evt) => setTitle(evt.target.value)}
|
||||
placeholder={t`Template Name`}
|
||||
/>
|
||||
<select
|
||||
value={selectedCanvas}
|
||||
onChange={(e) => {
|
||||
const sel = e.target;
|
||||
selectCanvas(sel.options[sel.selectedIndex].value);
|
||||
}}
|
||||
>
|
||||
{Object.keys(canvases).filter((c) => !canvases[c].v).map((canvas) => (
|
||||
<option key={canvas} value={canvas}>
|
||||
{canvases[canvas].title}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
<input
|
||||
type="text"
|
||||
style={{
|
||||
display: 'inline-block',
|
||||
width: '100%',
|
||||
maxWidth: '15em',
|
||||
}}
|
||||
placeholder="X_Y or URL"
|
||||
onChange={(evt) => {
|
||||
let co = evt.target.value.trim();
|
||||
co = coordsFromUrl(co) || co;
|
||||
evt.target.value = co;
|
||||
const newCoords = co.split('_').map((z) => parseInt(z, 10));
|
||||
setCoords((!newCoords.some(Number.isNaN) && newCoords.length === 2)
|
||||
? newCoords : null,
|
||||
);
|
||||
}}
|
||||
/>
|
||||
<button type="submit" disabled={!canSubmit}>{t`Save`}</button>
|
||||
</form>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default React.memo(AddTemplate);
|
|
@ -127,11 +127,14 @@ const TemplateItemEdit = ({
|
|||
selectCanvas(sel.options[sel.selectedIndex].value);
|
||||
}}
|
||||
>
|
||||
{Object.keys(canvases).filter((c) => !canvases[c].v).map((canvas) => (
|
||||
{Object.keys(canvases)
|
||||
.filter((c) => !canvases[c].v && !canvases[c].ed)
|
||||
.map((canvas) => (
|
||||
<option key={canvas} value={canvas}>
|
||||
{canvases[canvas].title}
|
||||
</option>
|
||||
))}
|
||||
),
|
||||
)}
|
||||
</select></span>
|
||||
</p>
|
||||
<p>
|
||||
|
|
|
@ -7,7 +7,6 @@ import { useSelector } from 'react-redux';
|
|||
import fileDownload from 'js-file-download';
|
||||
import { t } from 'ttag';
|
||||
|
||||
import AddTemplate from './AddTemplate';
|
||||
import TemplateItem from './TemplateItem';
|
||||
import TemplateItemEdit from './TemplateItemEdit';
|
||||
import templateLoader from '../ui/templateLoader';
|
||||
|
|
|
@ -706,7 +706,7 @@ export function bufferToBase64(array) {
|
|||
}
|
||||
|
||||
export async function base64ToBuffer(base64) {
|
||||
const dataUrl = "data:application/octet-binary;base64," + base64;
|
||||
const dataUrl = `data:application/octet-binary;base64,${base64}`;
|
||||
const res = await fetch(dataUrl);
|
||||
return res.arrayBuffer();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import {
|
|||
renderGrid,
|
||||
renderPlaceholder,
|
||||
renderPotatoPlaceholder,
|
||||
renderOverlay,
|
||||
} from './render2Delements';
|
||||
import PixelPainterControls from '../controls/PixelPainterControls';
|
||||
|
||||
|
@ -272,6 +273,7 @@ class Renderer2D extends Renderer {
|
|||
|
||||
const [x, y] = getPixelFromChunkOffset(i, j, offset, canvasSize);
|
||||
|
||||
// TODO centerChunk is scaled!
|
||||
const [canX, canY] = this.centerChunk
|
||||
.map((z) => (z + 0.5) * TILE_SIZE - canvasSize / 2);
|
||||
const { width: canvasWidth, height: canvasHeight } = this.canvas;
|
||||
|
@ -340,9 +342,7 @@ class Renderer2D extends Renderer {
|
|||
viewport,
|
||||
viewscale: scale,
|
||||
} = this;
|
||||
const {
|
||||
canvasSize,
|
||||
} = state.canvas;
|
||||
const { canvasSize } = state.canvas;
|
||||
|
||||
let { relScale } = this;
|
||||
|
||||
|
@ -422,6 +422,11 @@ class Renderer2D extends Renderer {
|
|||
}
|
||||
}
|
||||
context.restore();
|
||||
// TODO conditions
|
||||
renderOverlay(
|
||||
this.canvas, chunkPosition, canvasSize, scale,
|
||||
this.tiledScale, this.scaleThreshold,
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -3,7 +3,9 @@
|
|||
*
|
||||
*/
|
||||
|
||||
import templateLoader from './templateLoader';
|
||||
import { screenToWorld, worldToScreen } from '../core/utils';
|
||||
import { TILE_SIZE } from '../core/constants';
|
||||
|
||||
const PLACEHOLDER_SIZE = 1.2;
|
||||
const PLACEHOLDER_BORDER = 1;
|
||||
|
@ -100,3 +102,46 @@ export function renderGrid(
|
|||
|
||||
viewportCtx.globalAlpha = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Overlay draws onto offscreen canvas, so its doing weirder math
|
||||
*/
|
||||
export function renderOverlay(
|
||||
$canvas,
|
||||
centerChunk,
|
||||
canvasSize,
|
||||
scale,
|
||||
tiledScale,
|
||||
scaleThreshold,
|
||||
) {
|
||||
// world coordinates of center of center chunk
|
||||
const [x, y] = centerChunk
|
||||
.map((z) => z * TILE_SIZE / tiledScale
|
||||
+ TILE_SIZE / 2 / tiledScale - canvasSize / 2);
|
||||
const { width, height } = $canvas;
|
||||
const horizontalRadius = width / 2 / scale;
|
||||
const verticalRadius = height / 2 / scale;
|
||||
const templates = templateLoader.getTemplatesInView(
|
||||
x, y, horizontalRadius, verticalRadius,
|
||||
);
|
||||
|
||||
if (!templates.length) return;
|
||||
const context = $canvas.getContext('2d');
|
||||
if (!context) return;
|
||||
|
||||
// if scale > scaleThreshold, then scaling happens in renderer
|
||||
// instead of offscreen canvas
|
||||
const offscreenScale = (scale > scaleThreshold) ? 1.0 : scale;
|
||||
|
||||
context.save();
|
||||
context.scale(offscreenScale, offscreenScale);
|
||||
for (const template of templates) {
|
||||
const image = templateLoader.getTemplateSync(template.imageId);
|
||||
if (!image) continue;
|
||||
context.drawImage(image,
|
||||
template.x - x + width / 2 / offscreenScale,
|
||||
template.y - y + height / 2 / offscreenScale,
|
||||
);
|
||||
}
|
||||
context.restore();
|
||||
}
|
||||
|
|
|
@ -50,11 +50,35 @@ class TemplateLoader {
|
|||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
getTemplatesInView() {
|
||||
this.#store.templates
|
||||
getTemplateSync(id) {
|
||||
if (!this.ready) {
|
||||
return null;
|
||||
}
|
||||
const template = this.#templates.get(id);
|
||||
if (template) {
|
||||
return template.image;
|
||||
}
|
||||
// TODO some store action when available
|
||||
this.loadExistingTemplate(id);
|
||||
return null;
|
||||
}
|
||||
|
||||
getTemplatesInView(x, y, horizontalRadius, verticalRadius) {
|
||||
const topX = x - horizontalRadius;
|
||||
const topY = y - verticalRadius;
|
||||
const bottomX = x + horizontalRadius;
|
||||
const bottomY = y + verticalRadius;
|
||||
|
||||
const templates = [];
|
||||
this.#store.getState().templates.list.forEach((template) => {
|
||||
if (x < bottomX && y < bottomY
|
||||
&& x + template.width > topX && y + template.height > topY
|
||||
) {
|
||||
templates.push(template);
|
||||
}
|
||||
});
|
||||
return templates;
|
||||
}
|
||||
*/
|
||||
|
||||
/*
|
||||
* sync database to store,
|
||||
|
@ -152,7 +176,7 @@ class TemplateLoader {
|
|||
|
||||
deleteTemplate(title) {
|
||||
const { list } = this.#store.getState().templates;
|
||||
const tData = list.find((z) => z.title === title)
|
||||
const tData = list.find((z) => z.title === title);
|
||||
if (!tData) {
|
||||
return;
|
||||
}
|
||||
|
@ -197,7 +221,9 @@ class TemplateLoader {
|
|||
const imageIdList = await this.#fileStorage.saveFile(fileList);
|
||||
const idsToDelete = [];
|
||||
for (let i = 0; i < tDataList.length; i += 1) {
|
||||
const { x, y, width, height, canvasId, title } = tDataList[i];
|
||||
const {
|
||||
x, y, width, height, canvasId, title,
|
||||
} = tDataList[i];
|
||||
const imageId = imageIdList[i];
|
||||
const existing = list.find((z) => z.title === title);
|
||||
if (existing) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user