修改
This commit is contained in:
+335
-70
@@ -2,9 +2,9 @@
|
||||
"use strict";
|
||||
|
||||
const MARKER_TYPES = {
|
||||
entry: { label: "入场", color: "#00ff00", direction: "up" },
|
||||
exit: { label: "出场", color: "#0099ff", direction: "down" },
|
||||
stop: { label: "止损", color: "#ff3333", direction: "down" },
|
||||
entry: { label: "入场", color: "#00ff00" },
|
||||
exit: { label: "出场", color: "#0099ff" },
|
||||
stop: { label: "止损", color: "#ff3333" },
|
||||
};
|
||||
|
||||
const ARROW_HEAD = 12;
|
||||
@@ -12,11 +12,15 @@
|
||||
const ARROW_WIDTH = 9;
|
||||
const LINE_WIDTH = 2;
|
||||
const HIT_RADIUS = 18;
|
||||
const ZOOM_MIN = 0.25;
|
||||
const ZOOM_MAX = 5;
|
||||
const ZOOM_STEP = 0.15;
|
||||
|
||||
const fileInput = document.getElementById("file-input");
|
||||
const dropZone = document.getElementById("drop-zone");
|
||||
const dropPlaceholder = document.getElementById("drop-placeholder");
|
||||
const canvasWrap = document.getElementById("canvas-wrap");
|
||||
const viewport = document.getElementById("viewport");
|
||||
const stage = document.getElementById("stage");
|
||||
const chartImage = document.getElementById("chart-image");
|
||||
const canvas = document.getElementById("overlay-canvas");
|
||||
const ctx = canvas.getContext("2d");
|
||||
@@ -25,26 +29,70 @@
|
||||
const btnDownload = document.getElementById("btn-download");
|
||||
const statusHint = document.getElementById("status-hint");
|
||||
const modeButtons = document.querySelectorAll(".mode-btn");
|
||||
const zoomGroup = document.getElementById("zoom-group");
|
||||
const zoomLabel = document.getElementById("zoom-label");
|
||||
const btnZoomIn = document.getElementById("zoom-in");
|
||||
const btnZoomOut = document.getElementById("zoom-out");
|
||||
const btnZoomFit = document.getElementById("zoom-fit");
|
||||
const directionRow = document.getElementById("direction-row");
|
||||
const dirButtons = document.querySelectorAll(".dir-btn");
|
||||
const dirCenter = document.getElementById("dir-center");
|
||||
const angleSlider = document.getElementById("angle-slider");
|
||||
const angleInput = document.getElementById("angle-input");
|
||||
|
||||
let currentMode = "entry";
|
||||
let pendingAngle = 0;
|
||||
let markers = [];
|
||||
let displayWidth = 0;
|
||||
let displayHeight = 0;
|
||||
let baseWidth = 0;
|
||||
let baseHeight = 0;
|
||||
let zoom = 1;
|
||||
let imageLoaded = false;
|
||||
let dragIndex = -1;
|
||||
let selectedIndex = -1;
|
||||
let isDragging = false;
|
||||
let didDragMove = false;
|
||||
let isPanning = false;
|
||||
let panStartX = 0;
|
||||
let panStartY = 0;
|
||||
let panScrollLeft = 0;
|
||||
let panScrollTop = 0;
|
||||
|
||||
function setHint(text) {
|
||||
statusHint.textContent = text;
|
||||
}
|
||||
|
||||
function normalizeAngle(deg) {
|
||||
let a = Math.round(deg) % 360;
|
||||
if (a < 0) a += 360;
|
||||
return a;
|
||||
}
|
||||
|
||||
function updateButtons() {
|
||||
const hasImage = imageLoaded;
|
||||
const hasMarkers = markers.length > 0;
|
||||
btnUndo.disabled = !hasImage || !hasMarkers;
|
||||
btnClear.disabled = !hasImage || !hasMarkers;
|
||||
btnDownload.disabled = !hasImage;
|
||||
btnZoomIn.disabled = !hasImage;
|
||||
btnZoomOut.disabled = !hasImage;
|
||||
btnZoomFit.disabled = !hasImage;
|
||||
}
|
||||
|
||||
function getStageSize() {
|
||||
return {
|
||||
w: baseWidth * zoom,
|
||||
h: baseHeight * zoom,
|
||||
};
|
||||
}
|
||||
|
||||
function ratioToXY(m) {
|
||||
const { w, h } = getStageSize();
|
||||
return { x: m.xRatio * w, y: m.yRatio * h };
|
||||
}
|
||||
|
||||
function xyToRatio(x, y) {
|
||||
const { w, h } = getStageSize();
|
||||
return { xRatio: x / w, yRatio: y / h };
|
||||
}
|
||||
|
||||
function getCanvasPoint(event) {
|
||||
@@ -57,10 +105,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
function getMarkerDirection(type) {
|
||||
return MARKER_TYPES[type].direction;
|
||||
}
|
||||
|
||||
function dist(x1, y1, x2, y2) {
|
||||
const dx = x1 - x2;
|
||||
const dy = y1 - y2;
|
||||
@@ -69,77 +113,185 @@
|
||||
|
||||
function findMarkerAt(x, y) {
|
||||
for (let i = markers.length - 1; i >= 0; i--) {
|
||||
if (dist(x, y, markers[i].x, markers[i].y) <= HIT_RADIUS) {
|
||||
const p = ratioToXY(markers[i]);
|
||||
if (dist(x, y, p.x, p.y) <= HIT_RADIUS) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
function drawArrowMarker(targetCtx, x, y, type, color) {
|
||||
const direction = getMarkerDirection(type);
|
||||
function drawArrowMarker(targetCtx, x, y, angleDeg, color, highlight) {
|
||||
const w = ARROW_WIDTH;
|
||||
const h = ARROW_HEAD;
|
||||
const stem = ARROW_STEM;
|
||||
const rad = (angleDeg * Math.PI) / 180;
|
||||
|
||||
targetCtx.save();
|
||||
targetCtx.translate(x, y);
|
||||
targetCtx.rotate(rad);
|
||||
|
||||
targetCtx.fillStyle = color;
|
||||
targetCtx.strokeStyle = color;
|
||||
targetCtx.lineWidth = LINE_WIDTH;
|
||||
targetCtx.lineWidth = highlight ? LINE_WIDTH + 1 : LINE_WIDTH;
|
||||
targetCtx.lineCap = "round";
|
||||
targetCtx.lineJoin = "round";
|
||||
|
||||
if (direction === "up") {
|
||||
targetCtx.beginPath();
|
||||
targetCtx.moveTo(0, 0);
|
||||
targetCtx.lineTo(-w, h);
|
||||
targetCtx.lineTo(w, h);
|
||||
targetCtx.closePath();
|
||||
targetCtx.fill();
|
||||
|
||||
targetCtx.beginPath();
|
||||
targetCtx.moveTo(0, h);
|
||||
targetCtx.lineTo(0, h + stem);
|
||||
targetCtx.stroke();
|
||||
|
||||
if (highlight) {
|
||||
targetCtx.beginPath();
|
||||
targetCtx.moveTo(x, y);
|
||||
targetCtx.lineTo(x - w, y + h);
|
||||
targetCtx.lineTo(x + w, y + h);
|
||||
targetCtx.closePath();
|
||||
targetCtx.fill();
|
||||
targetCtx.beginPath();
|
||||
targetCtx.moveTo(x, y + h);
|
||||
targetCtx.lineTo(x, y + h + stem);
|
||||
targetCtx.stroke();
|
||||
} else {
|
||||
targetCtx.beginPath();
|
||||
targetCtx.moveTo(x, y);
|
||||
targetCtx.lineTo(x - w, y - h);
|
||||
targetCtx.lineTo(x + w, y - h);
|
||||
targetCtx.closePath();
|
||||
targetCtx.fill();
|
||||
targetCtx.beginPath();
|
||||
targetCtx.moveTo(x, y - h);
|
||||
targetCtx.lineTo(x, y - h - stem);
|
||||
targetCtx.arc(0, 0, HIT_RADIUS * 0.55, 0, Math.PI * 2);
|
||||
targetCtx.strokeStyle = "rgba(255,255,255,0.45)";
|
||||
targetCtx.lineWidth = 1.5;
|
||||
targetCtx.setLineDash([4, 3]);
|
||||
targetCtx.stroke();
|
||||
targetCtx.setLineDash([]);
|
||||
}
|
||||
|
||||
targetCtx.restore();
|
||||
}
|
||||
|
||||
function redraw() {
|
||||
if (!imageLoaded) return;
|
||||
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
||||
for (const m of markers) {
|
||||
drawArrowMarker(ctx, m.x, m.y, m.type, m.color);
|
||||
markers.forEach(function (m, i) {
|
||||
const p = ratioToXY(m);
|
||||
drawArrowMarker(
|
||||
ctx,
|
||||
p.x,
|
||||
p.y,
|
||||
m.angle,
|
||||
m.color,
|
||||
i === selectedIndex
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
function updateZoomLabel() {
|
||||
zoomLabel.textContent = Math.round(zoom * 100) + "%";
|
||||
}
|
||||
|
||||
function calculateBaseSize() {
|
||||
const maxW = Math.max(viewport.clientWidth - 8, 320);
|
||||
const maxH = Math.max(viewport.clientHeight - 8, 280);
|
||||
const natW = chartImage.naturalWidth;
|
||||
const natH = chartImage.naturalHeight;
|
||||
const scale = Math.min(maxW / natW, maxH / natH);
|
||||
baseWidth = natW * scale;
|
||||
baseHeight = natH * scale;
|
||||
}
|
||||
|
||||
function applyLayout() {
|
||||
if (!imageLoaded) return;
|
||||
const { w, h } = getStageSize();
|
||||
const cw = Math.round(w);
|
||||
const ch = Math.round(h);
|
||||
|
||||
stage.style.width = cw + "px";
|
||||
stage.style.height = ch + "px";
|
||||
chartImage.style.width = cw + "px";
|
||||
chartImage.style.height = ch + "px";
|
||||
canvas.width = cw;
|
||||
canvas.height = ch;
|
||||
canvas.style.width = cw + "px";
|
||||
canvas.style.height = ch + "px";
|
||||
|
||||
updateZoomLabel();
|
||||
redraw();
|
||||
}
|
||||
|
||||
function setZoom(newZoom, clientX, clientY) {
|
||||
const oldZoom = zoom;
|
||||
newZoom = Math.max(ZOOM_MIN, Math.min(ZOOM_MAX, newZoom));
|
||||
if (Math.abs(newZoom - oldZoom) < 0.001) return;
|
||||
|
||||
const rect = viewport.getBoundingClientRect();
|
||||
const hasPointer =
|
||||
typeof clientX === "number" && typeof clientY === "number";
|
||||
const scrollX = viewport.scrollLeft;
|
||||
const scrollY = viewport.scrollTop;
|
||||
const px = hasPointer ? clientX - rect.left + scrollX : scrollX + rect.width / 2;
|
||||
const py = hasPointer ? clientY - rect.top + scrollY : scrollY + rect.height / 2;
|
||||
const ratioX = px / (baseWidth * oldZoom);
|
||||
const ratioY = py / (baseHeight * oldZoom);
|
||||
|
||||
zoom = newZoom;
|
||||
applyLayout();
|
||||
|
||||
if (hasPointer) {
|
||||
viewport.scrollLeft = ratioX * baseWidth * zoom - (clientX - rect.left);
|
||||
viewport.scrollTop = ratioY * baseHeight * zoom - (clientY - rect.top);
|
||||
}
|
||||
}
|
||||
|
||||
function syncCanvasSize() {
|
||||
displayWidth = chartImage.offsetWidth;
|
||||
displayHeight = chartImage.offsetHeight;
|
||||
canvas.width = Math.round(displayWidth);
|
||||
canvas.height = Math.round(displayHeight);
|
||||
canvas.style.width = displayWidth + "px";
|
||||
canvas.style.height = displayHeight + "px";
|
||||
function resetZoomFit() {
|
||||
zoom = 1;
|
||||
applyLayout();
|
||||
viewport.scrollLeft = 0;
|
||||
viewport.scrollTop = 0;
|
||||
}
|
||||
|
||||
function syncAngleUI(angle) {
|
||||
const a = normalizeAngle(angle);
|
||||
pendingAngle = a;
|
||||
angleSlider.value = String(a);
|
||||
angleInput.value = String(a);
|
||||
dirCenter.textContent = a + "°";
|
||||
dirButtons.forEach(function (btn) {
|
||||
const btnAngle = parseInt(btn.dataset.angle, 10);
|
||||
btn.classList.toggle("active", btnAngle === a);
|
||||
});
|
||||
}
|
||||
|
||||
function applyAngleToTarget(angle) {
|
||||
const a = normalizeAngle(angle);
|
||||
syncAngleUI(a);
|
||||
if (selectedIndex >= 0) {
|
||||
markers[selectedIndex].angle = a;
|
||||
redraw();
|
||||
setHint("已更新选中标记方向为 " + a + "°");
|
||||
}
|
||||
}
|
||||
|
||||
function selectMarker(index) {
|
||||
selectedIndex = index;
|
||||
if (index >= 0) {
|
||||
syncAngleUI(markers[index].angle);
|
||||
setHint(
|
||||
"已选中标记,可调整方向或拖拽移动;点击空白处添加新标记"
|
||||
);
|
||||
}
|
||||
redraw();
|
||||
}
|
||||
|
||||
function clearAnnotations() {
|
||||
markers = [];
|
||||
dragIndex = -1;
|
||||
selectedIndex = -1;
|
||||
isDragging = false;
|
||||
didDragMove = false;
|
||||
redraw();
|
||||
updateButtons();
|
||||
}
|
||||
|
||||
function showEditorUI(show) {
|
||||
dropPlaceholder.classList.toggle("hidden", show);
|
||||
viewport.classList.toggle("hidden", !show);
|
||||
zoomGroup.hidden = !show;
|
||||
directionRow.hidden = !show;
|
||||
}
|
||||
|
||||
function loadImageFile(file) {
|
||||
if (!file) return;
|
||||
const validTypes = ["image/jpeg", "image/png"];
|
||||
@@ -154,16 +306,20 @@
|
||||
reader.onload = function (e) {
|
||||
chartImage.onload = function () {
|
||||
imageLoaded = true;
|
||||
dropPlaceholder.classList.add("hidden");
|
||||
canvasWrap.classList.remove("hidden");
|
||||
showEditorUI(true);
|
||||
dropZone.classList.remove("drag-over");
|
||||
zoom = 1;
|
||||
clearAnnotations();
|
||||
syncAngleUI(0);
|
||||
requestAnimationFrame(function () {
|
||||
syncCanvasSize();
|
||||
calculateBaseSize();
|
||||
applyLayout();
|
||||
viewport.scrollLeft = 0;
|
||||
viewport.scrollTop = 0;
|
||||
setHint(
|
||||
"当前模式:" +
|
||||
"滚轮缩放 · 右键/中键拖动画布 · 设置方向后单击添加 " +
|
||||
MARKER_TYPES[currentMode].label +
|
||||
" — 在图上单击添加箭头标记,可拖拽调整"
|
||||
" 标记"
|
||||
);
|
||||
updateButtons();
|
||||
});
|
||||
@@ -171,6 +327,7 @@
|
||||
chartImage.onerror = function () {
|
||||
setHint("图片加载失败,请换一张重试");
|
||||
imageLoaded = false;
|
||||
showEditorUI(false);
|
||||
updateButtons();
|
||||
};
|
||||
chartImage.src = e.target.result;
|
||||
@@ -184,15 +341,25 @@
|
||||
function addMarker(x, y) {
|
||||
const type = currentMode;
|
||||
const info = MARKER_TYPES[type];
|
||||
markers.push({ x: x, y: y, type: type, color: info.color });
|
||||
const ratio = xyToRatio(x, y);
|
||||
markers.push({
|
||||
xRatio: ratio.xRatio,
|
||||
yRatio: ratio.yRatio,
|
||||
type: type,
|
||||
color: info.color,
|
||||
angle: pendingAngle,
|
||||
});
|
||||
selectedIndex = markers.length - 1;
|
||||
redraw();
|
||||
updateButtons();
|
||||
setHint(
|
||||
"已添加 " +
|
||||
info.label +
|
||||
" 标记(共 " +
|
||||
"(" +
|
||||
pendingAngle +
|
||||
"°,共 " +
|
||||
markers.length +
|
||||
" 个)— 可切换模式继续标注"
|
||||
" 个)"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -201,8 +368,6 @@
|
||||
|
||||
const natW = chartImage.naturalWidth;
|
||||
const natH = chartImage.naturalHeight;
|
||||
const scaleX = natW / displayWidth;
|
||||
const scaleY = natH / displayHeight;
|
||||
|
||||
const exportCanvas = document.createElement("canvas");
|
||||
exportCanvas.width = natW;
|
||||
@@ -214,10 +379,11 @@
|
||||
for (const m of markers) {
|
||||
drawArrowMarker(
|
||||
exCtx,
|
||||
m.x * scaleX,
|
||||
m.y * scaleY,
|
||||
m.type,
|
||||
m.color
|
||||
m.xRatio * natW,
|
||||
m.yRatio * natH,
|
||||
m.angle,
|
||||
m.color,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
@@ -251,6 +417,7 @@
|
||||
}
|
||||
|
||||
function updateCursor(x, y) {
|
||||
if (isPanning) return;
|
||||
const idx = findMarkerAt(x, y);
|
||||
if (idx >= 0) {
|
||||
canvas.classList.add("can-drag");
|
||||
@@ -270,17 +437,65 @@
|
||||
didDragMove = false;
|
||||
if (imageLoaded) {
|
||||
setHint(
|
||||
"当前模式:" +
|
||||
"当前:" +
|
||||
MARKER_TYPES[currentMode].label +
|
||||
" — 单击添加箭头,可连续标注多个点位"
|
||||
" · 方向 " +
|
||||
pendingAngle +
|
||||
"° · 单击添加标记"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
dirButtons.forEach(function (btn) {
|
||||
btn.addEventListener("click", function () {
|
||||
applyAngleToTarget(parseInt(btn.dataset.angle, 10));
|
||||
});
|
||||
});
|
||||
|
||||
angleSlider.addEventListener("input", function () {
|
||||
applyAngleToTarget(parseInt(angleSlider.value, 10));
|
||||
});
|
||||
|
||||
angleInput.addEventListener("change", function () {
|
||||
let v = parseInt(angleInput.value, 10);
|
||||
if (Number.isNaN(v)) v = 0;
|
||||
applyAngleToTarget(v);
|
||||
});
|
||||
|
||||
btnZoomIn.addEventListener("click", function () {
|
||||
const rect = viewport.getBoundingClientRect();
|
||||
setZoom(
|
||||
zoom + ZOOM_STEP,
|
||||
rect.left + rect.width / 2,
|
||||
rect.top + rect.height / 2
|
||||
);
|
||||
});
|
||||
|
||||
btnZoomOut.addEventListener("click", function () {
|
||||
const rect = viewport.getBoundingClientRect();
|
||||
setZoom(
|
||||
zoom - ZOOM_STEP,
|
||||
rect.left + rect.width / 2,
|
||||
rect.top + rect.height / 2
|
||||
);
|
||||
});
|
||||
|
||||
btnZoomFit.addEventListener("click", resetZoomFit);
|
||||
|
||||
viewport.addEventListener(
|
||||
"wheel",
|
||||
function (e) {
|
||||
if (!imageLoaded) return;
|
||||
e.preventDefault();
|
||||
const delta = e.deltaY > 0 ? -ZOOM_STEP : ZOOM_STEP;
|
||||
setZoom(zoom + delta, e.clientX, e.clientY);
|
||||
},
|
||||
{ passive: false }
|
||||
);
|
||||
|
||||
fileInput.addEventListener("change", function () {
|
||||
const file = fileInput.files[0];
|
||||
loadImageFile(file);
|
||||
loadImageFile(fileInput.files[0]);
|
||||
fileInput.value = "";
|
||||
});
|
||||
|
||||
@@ -298,12 +513,29 @@
|
||||
dropZone.addEventListener("drop", function (e) {
|
||||
e.preventDefault();
|
||||
dropZone.classList.remove("drag-over");
|
||||
const file = e.dataTransfer.files[0];
|
||||
loadImageFile(file);
|
||||
loadImageFile(e.dataTransfer.files[0]);
|
||||
});
|
||||
|
||||
function startPan(e) {
|
||||
if (!imageLoaded) return;
|
||||
isPanning = true;
|
||||
panStartX = e.clientX;
|
||||
panStartY = e.clientY;
|
||||
panScrollLeft = viewport.scrollLeft;
|
||||
panScrollTop = viewport.scrollTop;
|
||||
viewport.classList.add("is-panning");
|
||||
e.preventDefault();
|
||||
}
|
||||
|
||||
canvas.addEventListener("mousedown", function (e) {
|
||||
if (!imageLoaded) return;
|
||||
|
||||
if (e.button === 1 || e.button === 2) {
|
||||
startPan(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.button !== 0) return;
|
||||
e.preventDefault();
|
||||
const pt = getCanvasPoint(e);
|
||||
const idx = findMarkerAt(pt.x, pt.y);
|
||||
@@ -311,18 +543,32 @@
|
||||
dragIndex = idx;
|
||||
isDragging = true;
|
||||
didDragMove = false;
|
||||
selectMarker(idx);
|
||||
canvas.classList.add("can-drag");
|
||||
} else {
|
||||
selectMarker(-1);
|
||||
}
|
||||
});
|
||||
|
||||
canvas.addEventListener("mousemove", function (e) {
|
||||
if (!imageLoaded) return;
|
||||
|
||||
if (isPanning) {
|
||||
viewport.scrollLeft = panScrollLeft - (e.clientX - panStartX);
|
||||
viewport.scrollTop = panScrollTop - (e.clientY - panStartY);
|
||||
return;
|
||||
}
|
||||
|
||||
const pt = getCanvasPoint(e);
|
||||
|
||||
if (isDragging && dragIndex >= 0) {
|
||||
didDragMove = true;
|
||||
markers[dragIndex].x = Math.max(0, Math.min(canvas.width, pt.x));
|
||||
markers[dragIndex].y = Math.max(0, Math.min(canvas.height, pt.y));
|
||||
const ratio = xyToRatio(
|
||||
Math.max(0, Math.min(canvas.width, pt.x)),
|
||||
Math.max(0, Math.min(canvas.height, pt.y))
|
||||
);
|
||||
markers[dragIndex].xRatio = ratio.xRatio;
|
||||
markers[dragIndex].yRatio = ratio.yRatio;
|
||||
redraw();
|
||||
return;
|
||||
}
|
||||
@@ -330,17 +576,30 @@
|
||||
updateCursor(pt.x, pt.y);
|
||||
});
|
||||
|
||||
canvas.addEventListener("mouseup", function () {
|
||||
function endPan() {
|
||||
isPanning = false;
|
||||
viewport.classList.remove("is-panning");
|
||||
}
|
||||
|
||||
canvas.addEventListener("mouseup", function (e) {
|
||||
if (e.button === 1 || e.button === 2 || isPanning) {
|
||||
endPan();
|
||||
}
|
||||
isDragging = false;
|
||||
dragIndex = -1;
|
||||
});
|
||||
|
||||
canvas.addEventListener("mouseleave", function () {
|
||||
endPan();
|
||||
isDragging = false;
|
||||
dragIndex = -1;
|
||||
canvas.classList.remove("can-drag");
|
||||
});
|
||||
|
||||
canvas.addEventListener("contextmenu", function (e) {
|
||||
e.preventDefault();
|
||||
});
|
||||
|
||||
canvas.addEventListener("click", function (e) {
|
||||
if (!imageLoaded) return;
|
||||
if (didDragMove) {
|
||||
@@ -355,6 +614,9 @@
|
||||
btnUndo.addEventListener("click", function () {
|
||||
if (markers.length === 0) return;
|
||||
markers.pop();
|
||||
if (selectedIndex >= markers.length) {
|
||||
selectedIndex = markers.length - 1;
|
||||
}
|
||||
redraw();
|
||||
updateButtons();
|
||||
setHint(
|
||||
@@ -374,11 +636,14 @@
|
||||
btnDownload.addEventListener("click", exportImage);
|
||||
|
||||
window.addEventListener("resize", function () {
|
||||
if (imageLoaded) {
|
||||
syncCanvasSize();
|
||||
}
|
||||
if (!imageLoaded) return;
|
||||
calculateBaseSize();
|
||||
applyLayout();
|
||||
});
|
||||
|
||||
dropPlaceholder.classList.remove("hidden");
|
||||
window.addEventListener("mouseup", endPan);
|
||||
|
||||
showEditorUI(false);
|
||||
updateButtons();
|
||||
syncAngleUI(0);
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user