feat(编辑能力): 统一文本框自适应与拖拽调整

This commit is contained in:
xiaoqidun committed 2026-10-28 09:44:33 +08:00
1 parent da3f64c74a
commit a75b90dffc
4 files changed
+40 -21

No files matched your search

+5 -7
View File
@@ -3968,7 +3968,7 @@ async function editCanvasObject(item, point = null) {
if (!isForegroundDocument(tab) || openSeq !== state.openSeq) return;
let rich = null;
if (item.richText) rich = await loadRichText(item, face, tab, data.bytes);
else if (!item.scoped && layoutKnown) {
else if (layoutKnown) {
try { rich = await loadRichText(item, face, tab, data.bytes); } catch {}
}
if (!isForegroundDocument(tab) || openSeq !== state.openSeq) return;
@@ -4284,7 +4284,6 @@ async function toggleTextTool() {
const font = state.textFonts[Number(fontPicker.value)];
state.textDefaults = { type: "TextObject", size: style.size, color: style.color, wrap: style.wrap,
bold: Boolean(style.bold), italic: Boolean(style.italic), underline: Boolean(style.underline), strikeout: Boolean(style.strikeout), shadow: Boolean(style.shadow), background: style.background || "",
wrapOverride: style === state.textDefaults ? style.wrapOverride : style.wrap,
align: style.align || "left", paragraphHeight: style.paragraphHeight || 0, letterSpacing: style.letterSpacing || 0,
leftIndent: style.leftIndent || 0, rightIndent: style.rightIndent || 0, firstLineIndent: style.firstLineIndent || 0,
fontChoice: font && !font.disabled ? font : state.textDefaults.fontChoice };
@@ -4312,7 +4311,7 @@ async function beginCanvasText(index, box, surface, page) {
await face.load();
if (!isForegroundDocument(tab) || openSeq !== state.openSeq || !surface.isConnected) return;
const node = document.createElement("div");
const item = { ...state.textDefaults, ...box, wrap: state.textDefaults.wrapOverride ?? box.wrap ?? state.textDefaults.wrap,
const item = { ...state.textDefaults, ...box, wrap: Boolean(box.wrap),
index, page, surface, node, text: "", draft: true, fontData: data };
const model = { ...richTextModel(item), autoWidth: !box.wrap };
const key = `temp-font-${crypto.randomUUID()}`;
@@ -4578,7 +4577,6 @@ async function changeTextAlignment() {
async function changeParagraph(wrap) {
const item = currentTextStyle();
const explicitWrap = wrap !== undefined;
wrap ??= item.wrap;
if (document.body.hasAttribute("data-busy") || item.draft) return;
if (!el.textLineHeight.checkValidity()) {
@@ -4593,7 +4591,6 @@ async function changeParagraph(wrap) {
const spacing = inputMillimeters(el.textSpacing, item.letterSpacing || 0);
if (item === state.textDefaults) {
Object.assign(item, { wrap: Boolean(wrap), align: el.textAlign.value, paragraphHeight: height, letterSpacing: spacing });
if (explicitWrap) item.wrapOverride = Boolean(wrap);
updateObjectControls(canvasEditor.selected, true);
return;
}
@@ -12490,12 +12487,13 @@ function updateObjectControls(item, reset = false) {
el.undoButton.disabled = !state.editorInfo?.canUndo || !state.ready || state.exporting;
el.redoButton.disabled = !state.editorInfo?.canRedo || !state.ready || state.exporting;
}
el.textWrap.disabled = el.textLineHeight.disabled = el.textSpacing.disabled = el.textSize.disabled || Boolean(item?.items || item?.draft) || Boolean(canvasEditor.input);
el.textLineHeight.disabled = el.textSpacing.disabled = el.textSize.disabled || Boolean(item?.items || item?.draft) || Boolean(canvasEditor.input);
el.textWrap.disabled = el.textLineHeight.disabled || text === state.textDefaults;
const reflow = document.querySelector("#textReflow"), positioned = canvasEditor.input?.source && (item?.selectionIDs || !canEditObject(item, "layoutKnown"));
reflow.hidden = !positioned;
el.textWrap.hidden = !!positioned;
reflow.disabled = textDisabled || !canEditObject(item, "reflow") || !!canvasEditor.input?.reflow;
el.paragraphButton.disabled = el.textWrap.disabled;
el.paragraphButton.disabled = el.textLineHeight.disabled;
el.textAlign.disabled = el.textSize.disabled || Boolean(item?.items || item?.selectionIDs);
const layoutKnown = text === state.textDefaults || Boolean(item?.draft) || canEditObject(item, "layoutKnown");
const rich = canvasEditor.input?.rich;
+11 -7
View File
@@ -11,6 +11,10 @@ function sourceTextScale(editing) {
return Math.max(Math.hypot(a, b), Math.hypot(c, d)) || 1;
}
function textFrameBox(item) {
return { x: 0, y: 0, width: item.paragraphWidth ?? item.textFrame.width, height: item.textFrame.height };
}
export function fontFaceStyle(data) {
const style = { weight: "400", style: "normal" };
const bytes = ArrayBuffer.isView(data) ? new DataView(data.buffer, data.byteOffset, data.byteLength) : new DataView(data);
@@ -936,8 +940,8 @@ export class CanvasEditor {
});
if (item.contour) item.contour.svg.setAttribute("viewBox", `${item.x} ${item.y} ${item.width} ${item.height}`);
if (item.textFrame) {
const { matrix: [a, b, c, d, e, f], width, height } = item.textFrame;
const box = item.textFrame.content || { x: 0, y: 0, width, height };
const [a, b, c, d, e, f] = item.textFrame.matrix;
const box = textFrameBox(item);
for (const handle of item.node.children) {
if (handle.dataset.corner !== "w" && handle.dataset.corner !== "e") continue;
const x = box.x + (handle.dataset.corner === "e" ? box.width : 0), y = box.y + box.height / 2;
@@ -1265,7 +1269,7 @@ export class CanvasEditor {
if (drag.item.textFrame) {
const frame = drag.item.textFrame, [a, b, c, d] = frame.matrix;
const dx = (d * (to.x - from.x) - c * (to.y - from.y)) / (a * d - b * c);
const box = reshapeBox({ geometry: frame.content || { x: 0, y: 0, width: frame.width, height: frame.height } }, dx, 0, drag.corner, false);
const box = reshapeBox({ geometry: textFrameBox(drag.item) }, dx, 0, drag.corner, false);
drag.box = { x: drag.item.x + box.x, width: box.width };
const bounds = transformedBox(box, frame.matrix);
Object.assign(drag.item.node.style, { left: `${bounds.x * PX_PER_MM}px`, top: `${bounds.y * PX_PER_MM}px`,
@@ -1429,7 +1433,7 @@ export class CanvasEditor {
this.drag = null;
this.viewer.releasePointerCapture(drag.pointerID);
const frame = drag.item.textFrame;
const geometry = drag.item.oriented?.box || drag.item.geometry || (frame ? { x: drag.item.x + (frame.content?.x || 0), width: frame.content?.width ?? frame.width } : drag.item);
const geometry = drag.item.oriented?.box || drag.item.geometry || (frame ? { x: drag.item.x, width: textFrameBox(drag.item).width } : drag.item);
const changed = Object.keys(drag.box).some((key) => drag.box[key] !== geometry[key]);
const apply = drag.item.shape || drag.item.oriented ? this.options.onReshape : canEditObject(drag.item, "stretch") ? this.options.onResize : this.options.onTextWidth;
Promise.resolve(changed && apply(drag.item, drag.box)).finally(() => {
@@ -1779,9 +1783,9 @@ export class CanvasEditor {
else paragraph.append(document.createElement("br"));
input.append(paragraph);
}
const left = item.leftIndent || 0;
const frame = { ...(item.textFrame || { width: item.width, height: item.height, matrix: [1, 0, 0, 1, item.x, item.y] }) };
if (item.wrap && item.paragraphWidth != null) frame.width = item.paragraphWidth;
const left = rich?.model.layout.leftIndent || item.leftIndent || 0;
const frame = { ...(rich?.frame || item.textFrame || { width: item.width, height: item.height, matrix: [1, 0, 0, 1, item.x, item.y] }) };
if (!rich?.frame && item.paragraphWidth != null) frame.width = item.paragraphWidth;
const [a, b, c, d, e, f] = frame.matrix;
Object.assign(input.style, {
left: "0px", top: "0px",