mirror of
https://github.com/xiaoqidun/ofdgo.git
synced 2026-10-30 01:27:26 +08:00
1 parent
4826496e98
commit
9f2ef7e212
18 files changed
+674
-79
No files matched your search
@@ -175,6 +175,7 @@
|
||||
</dialog>
|
||||
<dialog id="objectStylePanel" class="form-dialog" aria-label="对象样式">
|
||||
<form id="objectStyleForm">
|
||||
<div class="form-row"><span>组合</span><div class="form-format"><button id="groupObjectsButton" class="button" type="button">组合</button><button id="ungroupObjectButton" class="button" type="button">解组</button></div></div>
|
||||
<div class="form-row"><span>格式</span><div class="form-format"><button id="copyStyleButton" class="button" type="button" aria-label="复制格式">复制</button><button id="pasteStyleButton" class="button" type="button" aria-label="粘贴格式">粘贴</button></div></div>
|
||||
<div class="form-row"><label for="objectOpacity">透明</label><div class="form-unit"><input id="objectOpacity" type="number" min="0" max="100" step="any" aria-label="透明度" autofocus><span>%</span></div></div>
|
||||
<div id="objectStrokeFields">
|
||||
|
||||
+15
-3
@@ -203,6 +203,8 @@ const el = {
|
||||
objectAspect: document.querySelector("#objectAspect"),
|
||||
objectStylePanel: document.querySelector("#objectStylePanel"),
|
||||
objectStyleForm: document.querySelector("#objectStyleForm"),
|
||||
groupObjectsButton: document.querySelector("#groupObjectsButton"),
|
||||
ungroupObjectButton: document.querySelector("#ungroupObjectButton"),
|
||||
objectOpacity: document.querySelector("#objectOpacity"),
|
||||
objectStrokeFields: document.querySelector("#objectStrokeFields"),
|
||||
objectDash: document.querySelector("#objectDash"),
|
||||
@@ -366,7 +368,8 @@ const canvasEditor = new CanvasEditor(el.viewerPanel, {
|
||||
updatePendingChanges();
|
||||
el.undoButton.disabled = !state.ready || state.exporting || !state.editorInfo?.canUndo && !canvasEditor.nudgeChanged();
|
||||
},
|
||||
onReshape: (item, box) => changeDocument("ofdgoReshapeObject", item, box.x, box.y, box.width, box.height),
|
||||
onReshape: (item, box) => changeDocument("ofdgoReshapeObject", item, box.x, box.y, box.width, box.height, Boolean(item.oriented)),
|
||||
onResize: (item, box) => changeDocument("ofdgoResizeObjects", { ...item, id: [item.id] }, box.x, box.y, box.width, box.height),
|
||||
onTextWidth: (item, box) => changeDocument("ofdgoLayoutText", item, box.x - item.x, box.width, true, item.align || "", item.paragraphHeight || 0, item.letterSpacing || 0, ...textIndents(item)),
|
||||
onCrop: (item, box) => changeDocument("ofdgoCropImage", item, box.x, box.y, box.width, box.height),
|
||||
onCropChange: () => {
|
||||
@@ -658,6 +661,12 @@ el.batchPagesForm.addEventListener("submit", async event => {
|
||||
});
|
||||
editorClick(el.objectStyleButton, openObjectStyle);
|
||||
editorClick(el.objectBoundsButton, openObjectBounds);
|
||||
for (const [button, ungroup] of [[el.groupObjectsButton, false], [el.ungroupObjectButton, true]]) {
|
||||
button.addEventListener("click", async () => {
|
||||
const item = canvasEditor.selected;
|
||||
if (await changeDocument("ofdgoGroupObjects", { ...item, id: canvasEditor.items().map(member => member.id) }, ungroup)) el.objectStylePanel.close();
|
||||
});
|
||||
}
|
||||
el.arrowTool.addEventListener("change", async () => {
|
||||
const tool = el.arrowTool.value;
|
||||
el.arrowTool.value = "";
|
||||
@@ -999,7 +1008,7 @@ function openObjectBounds() {
|
||||
state.boundsOriginal = { x: box.x, y: box.y, width: Math.abs(box.width), height: Math.abs(box.height) };
|
||||
for (const [input, key] of [[el.objectX,"x"], [el.objectY,"y"], [el.objectWidth,"width"], [el.objectHeight,"height"]]) setObjectDimension(input, state.boundsOriginal[key]);
|
||||
el.objectAspect.checked = true;
|
||||
el.objectAspect.disabled = Boolean(state.composite) || !item.shape && (item.items || [item]).some(member => member.type !== "ImageObject" || member.imageBorder) || lineShape(item.shape) && (!box.width || !box.height);
|
||||
el.objectAspect.disabled = !item.shape && (item.items || [item]).some(member => member.type !== "ImageObject" || member.imageBorder) || lineShape(item.shape) && (!box.width || !box.height);
|
||||
el.objectWidth.disabled = lineShape(item.shape) && !box.width;
|
||||
el.objectHeight.disabled = lineShape(item.shape) && !box.height;
|
||||
el.objectBoundsStatus.textContent = "";
|
||||
@@ -1016,6 +1025,9 @@ function canCopyStyle(item) {
|
||||
function openObjectStyle() {
|
||||
const items = canvasEditor.items();
|
||||
if (!items.length) return;
|
||||
const ordered = [...items].sort((a, b) => a.position - b.position);
|
||||
el.groupObjectsButton.disabled = Boolean(state.composite) || items.length < 2 || !ordered.every((item, index) => canEditObject(item, "copy") && canEditObject(item, "order") && item.container === ordered[0].container && item.position === ordered[0].position + index);
|
||||
el.ungroupObjectButton.disabled = Boolean(state.composite) || items.length !== 1 || !canEditObject(items[0], "ungroup");
|
||||
el.copyStyleButton.disabled = items.length !== 1 || !canCopyStyle(items[0]);
|
||||
el.pasteStyleButton.disabled = !state.styleClipboard || !items.every(item => item.type === state.styleClipboard && canCopyStyle(item));
|
||||
const shared = (key, fallback) => items.every(item => (item[key] ?? fallback) === (items[0][key] ?? fallback)) ? items[0][key] ?? fallback : null;
|
||||
@@ -1926,7 +1938,7 @@ async function changeDocument(name, item, ...args) {
|
||||
return true;
|
||||
}
|
||||
const clearSelection = !item || ["ofdgoDeleteObject", "ofdgoDeleteObjects", "ofdgoEraseObjects", "ofdgoEraseObjectsPath"].includes(name);
|
||||
if (name === "ofdgoCopyObjects" || name === "ofdgoPasteObjects" || name === "ofdgoInsertShape" || name === "ofdgoInsertText" || name === "ofdgoInsertImage") {
|
||||
if (name === "ofdgoCopyObjects" || name === "ofdgoPasteObjects" || name === "ofdgoGroupObjects" || name === "ofdgoInsertShape" || name === "ofdgoInsertText" || name === "ofdgoInsertImage") {
|
||||
state.selectObjects = true;
|
||||
canvasEditor.setTool("");
|
||||
setPan(false);
|
||||
|
||||
@@ -159,6 +159,11 @@ export function reshapeBox(item, dx, dy, handle, shift) {
|
||||
return { x, y, width: right - x, height: bottom - y };
|
||||
}
|
||||
|
||||
export function reshapeFrame(frame, dx, dy, handle, shift) {
|
||||
const [a, b, c, d] = frame.matrix, determinant = a * d - b * c;
|
||||
return reshapeBox({ geometry: frame.box }, (d * dx - c * dy) / determinant, (a * dy - b * dx) / determinant, handle, shift);
|
||||
}
|
||||
|
||||
function paintShape(node, shape, box) {
|
||||
const { x, y, width, height } = box;
|
||||
const attributes = lineShape(shape) && shape !== "line" ? { d: arrowPath(shape, box) }
|
||||
@@ -289,9 +294,10 @@ export class CanvasEditor {
|
||||
svg.setAttribute("aria-hidden", "true");
|
||||
svg.setAttribute("preserveAspectRatio", "none");
|
||||
let contour;
|
||||
if (object.type === "PathObject" && (!object.scoped || object.shape)) {
|
||||
contour = document.createElementNS("http://www.w3.org/2000/svg", object.shape === "line" ? "line" : object.shape === "ellipse" ? "ellipse" : object.shape === "rectangle" ? "rect" : "path");
|
||||
if (!object.shape) contour.setAttribute("d", object.outline);
|
||||
if (object.type === "PathObject" && (!object.scoped || object.shape || object.oriented)) {
|
||||
const shape = object.shape || object.oriented?.kind;
|
||||
contour = document.createElementNS("http://www.w3.org/2000/svg", shape === "line" ? "line" : shape === "ellipse" ? "ellipse" : shape === "rectangle" ? "rect" : "path");
|
||||
if (!shape) contour.setAttribute("d", object.outline);
|
||||
svg.append(contour);
|
||||
} else {
|
||||
for (const path of object.contours) {
|
||||
@@ -306,7 +312,7 @@ export class CanvasEditor {
|
||||
this.nodes.set(node, item);
|
||||
node.addEventListener("focus", () => this.select(item));
|
||||
const handles = !canEditObject(object, "transform") ? [] : lineShape(object.shape) ? ["start", "end"]
|
||||
: object.shape ? ["nw", "n", "ne", "e", "se", "s", "sw", "w"]
|
||||
: object.shape || object.oriented || object.type === "ImageObject" && !object.imageBorder ? ["nw", "n", "ne", "e", "se", "s", "sw", "w"]
|
||||
: object.type === "TextObject" && canEditObject(object, "reflow") && canEditObject(object, "layoutKnown")
|
||||
? ["nw", "n", "ne", "e", "se", "s", "sw", "w"] : ["nw", "ne", "sw", "se"];
|
||||
for (const corner of handles) {
|
||||
@@ -346,6 +352,10 @@ export class CanvasEditor {
|
||||
if (x || y || scale !== 1) item.artwork.setAttribute("transform", `translate(${x} ${y}) scale(${scale})`);
|
||||
else item.artwork.removeAttribute("transform");
|
||||
}
|
||||
if (item.oriented) {
|
||||
this.placeOriented(item, item.oriented.box, change);
|
||||
return;
|
||||
}
|
||||
if (item.shape) {
|
||||
this.placeShape(item, { x: item.geometry.x + change.x + (item.geometry.x - item.x) * (change.scale - 1),
|
||||
y: item.geometry.y + change.y + (item.geometry.y - item.y) * (change.scale - 1),
|
||||
@@ -369,6 +379,28 @@ export class CanvasEditor {
|
||||
}
|
||||
}
|
||||
|
||||
placeOriented(item, box, change = { x: 0, y: 0, scale: 1 }) {
|
||||
const frame = item.oriented, bounds = transformedBox(box, frame.matrix), scale = change.scale;
|
||||
const [a, b, c, d, e, f] = frame.matrix;
|
||||
Object.assign(item.node.style, {
|
||||
left: `${(item.x + change.x + (bounds.x - item.x) * scale) * PX_PER_MM}px`,
|
||||
top: `${(item.y + change.y + (bounds.y - item.y) * scale) * PX_PER_MM}px`,
|
||||
width: `${bounds.width * scale * PX_PER_MM}px`, height: `${bounds.height * scale * PX_PER_MM}px`,
|
||||
});
|
||||
for (const handle of item.node.children) {
|
||||
const corner = handle.dataset.corner;
|
||||
if (!corner) continue;
|
||||
const x = box.x + box.width * (corner.includes("w") ? 0 : corner.includes("e") ? 1 : .5);
|
||||
const y = box.y + box.height * (corner.includes("n") ? 0 : corner.includes("s") ? 1 : .5);
|
||||
handle.style.left = `${(a * x + c * y + e - bounds.x) * scale * PX_PER_MM}px`;
|
||||
handle.style.top = `${(b * x + d * y + f - bounds.y) * scale * PX_PER_MM}px`;
|
||||
}
|
||||
const { svg, path } = item.contour;
|
||||
svg.setAttribute("viewBox", `${bounds.x} ${bounds.y} ${bounds.width} ${bounds.height}`);
|
||||
path.setAttribute("transform", `matrix(${frame.matrix.join(" ")})`);
|
||||
paintShape(path, frame.kind, box);
|
||||
}
|
||||
|
||||
placeShape(item, box) {
|
||||
const x = Math.min(box.x, box.x + box.width), y = Math.min(box.y, box.y + box.height);
|
||||
Object.assign(item.node.style, {
|
||||
@@ -597,6 +629,23 @@ export class CanvasEditor {
|
||||
Object.assign(drag.item.node.style, { left: `${drag.box.x * PX_PER_MM}px`, width: `${drag.box.width * PX_PER_MM}px` });
|
||||
return;
|
||||
}
|
||||
if (drag.item.type === "ImageObject" && !drag.item.imageBorder && drag.corner.length === 1) {
|
||||
drag.box = reshapeBox({ geometry: drag.item }, to.x - from.x, to.y - from.y, drag.corner, false);
|
||||
const sx = drag.box.width / drag.item.width, sy = drag.box.height / drag.item.height;
|
||||
Object.assign(drag.item.node.style, { left: `${drag.box.x * PX_PER_MM}px`, top: `${drag.box.y * PX_PER_MM}px`,
|
||||
width: `${drag.box.width * PX_PER_MM}px`, height: `${drag.box.height * PX_PER_MM}px` });
|
||||
drag.item.artwork?.setAttribute("transform", `translate(${drag.box.x - drag.item.x * sx} ${drag.box.y - drag.item.y * sy}) scale(${sx} ${sy})`);
|
||||
return;
|
||||
}
|
||||
if (drag.item.oriented && drag.corner) {
|
||||
const frame = drag.item.oriented;
|
||||
drag.box = reshapeFrame(frame, to.x - from.x, to.y - from.y, drag.corner, event.shiftKey);
|
||||
this.placeOriented(drag.item, drag.box);
|
||||
const matrix = new DOMMatrix(frame.matrix), sx = drag.box.width / frame.box.width, sy = drag.box.height / frame.box.height;
|
||||
const transform = matrix.translate(drag.box.x - frame.box.x * sx, drag.box.y - frame.box.y * sy).scale(sx, sy).multiply(matrix.inverse());
|
||||
drag.item.artwork?.setAttribute("transform", `matrix(${[transform.a, transform.b, transform.c, transform.d, transform.e, transform.f].join(" ")})`);
|
||||
return;
|
||||
}
|
||||
if (drag.item.shape && drag.corner) {
|
||||
drag.box = reshapeBox(drag.item, to.x - from.x, to.y - from.y, drag.corner, event.shiftKey);
|
||||
this.placeShape(drag.item, drag.box || drag.item.geometry);
|
||||
@@ -709,12 +758,12 @@ export class CanvasEditor {
|
||||
.finally(() => drag.preview.remove());
|
||||
return;
|
||||
}
|
||||
if ((drag.item.shape || drag.item.type === "TextObject") && drag.corner && drag.box) {
|
||||
if (drag.corner && drag.box) {
|
||||
this.drag = null;
|
||||
this.viewer.releasePointerCapture(drag.pointerID);
|
||||
const geometry = drag.item.geometry || (drag.item.textFrame ? { x: drag.item.x, width: drag.item.textFrame.width } : drag.item);
|
||||
const geometry = drag.item.oriented?.box || drag.item.geometry || (drag.item.textFrame ? { x: drag.item.x, width: drag.item.textFrame.width } : drag.item);
|
||||
const changed = Object.keys(drag.box).some((key) => drag.box[key] !== geometry[key]);
|
||||
const apply = drag.item.shape ? this.options.onReshape : this.options.onTextWidth;
|
||||
const apply = drag.item.shape || drag.item.oriented ? this.options.onReshape : drag.item.type === "ImageObject" ? this.options.onResize : this.options.onTextWidth;
|
||||
Promise.resolve(changed && apply(drag.item, drag.box)).finally(() => {
|
||||
drag.preview?.remove();
|
||||
this.place(drag.item);
|
||||
|
||||
Reference in new issue
Block a user