diff --git a/packages/av-canvas/demo/video-editor.html b/packages/av-canvas/demo/video-editor.html index 9a2a1ada..7378d5a8 100644 --- a/packages/av-canvas/demo/video-editor.html +++ b/packages/av-canvas/demo/video-editor.html @@ -7,9 +7,14 @@ AVCanvas diff --git a/packages/av-canvas/src/sprites/sprite-op.ts b/packages/av-canvas/src/sprites/sprite-op.ts index 3429a575..bfcd5416 100644 --- a/packages/av-canvas/src/sprites/sprite-op.ts +++ b/packages/av-canvas/src/sprites/sprite-op.ts @@ -72,6 +72,8 @@ export function draggabelSprite( let startY = 0; let startRect: Rect | null = null; + const refline = createRefline(cvsEl, container); + let hitSpr: VisibleSprite | null = null; // sprMng.activeSprite 在 av-canvas.ts -> activeSprite 中被赋值 const onCvsMouseDown = (evt: MouseEvent): void => { @@ -97,14 +99,14 @@ export function draggabelSprite( startRect = hitSpr.rect.clone(); + refline.magneticEffect(hitSpr.rect.x, hitSpr.rect.y, hitSpr.rect); + startX = clientX; startY = clientY; window.addEventListener('mousemove', onMouseMove); window.addEventListener('mouseup', clearWindowEvt); }; - const refline = createRefline(cvsEl, container); - const onMouseMove = (evt: MouseEvent): void => { if (hitSpr == null || startRect == null) return; @@ -386,66 +388,113 @@ function updateRectWithSafeMargin( } /** - * 靠近中间时显示水平、垂直方向的中线参考线 - * 具有磁吸效果 + * 创建四周+中线参考线, 靠近具有磁吸效果 */ function createRefline(cvsEl: HTMLCanvasElement, container: HTMLElement) { - const baseCss = ` - display: none; + const reflineBaseCSS = `display: none; position: absolute;`; + const baseSettings = { w: 0, h: 0, x: 0, y: 0 } as const; + const reflineSettings: Record< + 'top' | 'bottom' | 'left' | 'right' | 'vertMiddle' | 'horMiddle', + { + // 四周加中线参考线,它们的坐标、宽高只能是 0 | 50 | 100 + w: 0 | 50 | 100; + h: 0 | 50 | 100; + x: 0 | 50 | 100; + y: 0 | 50 | 100; + ref: { prop: 'x' | 'y'; val: (rect: Rect) => number }; + } + > = { + vertMiddle: { + ...baseSettings, + h: 100, + x: 50, + ref: { prop: 'x', val: ({ w }) => (cvsEl.width - w) / 2 }, + }, + horMiddle: { + ...baseSettings, + w: 100, + y: 50, + ref: { prop: 'y', val: ({ h }) => (cvsEl.height - h) / 2 }, + }, + top: { + ...baseSettings, + w: 100, + ref: { prop: 'y', val: () => 0 }, + }, + bottom: { + ...baseSettings, + w: 100, + y: 100, + ref: { prop: 'y', val: ({ h }) => cvsEl.height - h }, + }, + left: { + ...baseSettings, + h: 100, + ref: { prop: 'x', val: () => 0 }, + }, + right: { + ...baseSettings, + h: 100, + x: 100, + ref: { prop: 'x', val: ({ w }) => cvsEl.width - w }, + }, + } as const; + + const lineWrap = createEl('div'); + lineWrap.style.cssText = ` position: absolute; + top: 0; left: 0; + width: 100%; height: 100%; + pointer-events: none; + box-sizing: border-box; `; - const xRefLine = createEl('div'); - xRefLine.style.cssText = ` - ${baseCss} - border-left: 1px solid #3ee; - top: 0; left: 50%; - width: 0; height: 100%; - `; - const yRefLine = createEl('div'); - yRefLine.style.cssText = ` - ${baseCss} - border-top: 1px solid #3ee; - top: 50%; left: 0; - width: 100%; height: 0; - `; - container.appendChild(xRefLine); - container.appendChild(yRefLine); - + const reflineEls = Object.fromEntries( + Object.entries(reflineSettings).map(([key, { w, h, x, y }]) => { + const lineEl = createEl('div'); + lineEl.style.cssText = ` + ${reflineBaseCSS} + border-${w > 0 ? 'top' : 'left'}: 1px solid #3ee; + top: ${y}%; left: ${x}%; + ${x === 100 ? 'margin-left: -1px' : ''}; + ${y === 100 ? 'margin-top: -1px' : ''}; + width: ${w}%; height: ${h}%; + `; + lineWrap.appendChild(lineEl); + return [key, lineEl]; + }), + ) as Record; + container.appendChild(lineWrap); + + const magneticDistance = 6 / (900 / cvsEl.width); return { magneticEffect(expectX: number, expectY: number, rect: Rect) { - const { center, x, y, w, h } = rect; - - let newX = expectX; - let newY = expectY; - if ( - Math.abs(center.x - cvsEl.width / 2) <= 5 && - Math.abs(expectX - x) <= 5 - ) { - xRefLine.style.display = 'block'; - newX = (cvsEl.width - w) / 2; - } else { - xRefLine.style.display = 'none'; - } - - if ( - Math.abs(center.y - cvsEl.height / 2) <= 5 && - Math.abs(expectY - y) <= 5 - ) { - yRefLine.style.display = 'block'; - newY = (cvsEl.height - h) / 2; - } else { - yRefLine.style.display = 'none'; + const retVal = { x: expectX, y: expectY }; + let reflineKey: keyof typeof reflineSettings; + let correctionState = { x: false, y: false }; + for (reflineKey in reflineSettings) { + const { prop, val } = reflineSettings[reflineKey].ref; + if (correctionState[prop]) continue; + + const refVal = val(rect); + if ( + Math.abs(rect[prop] - refVal) <= magneticDistance && + Math.abs(rect[prop] - (prop === 'x' ? expectX : expectY)) <= + magneticDistance + ) { + retVal[prop] = refVal; + reflineEls[reflineKey].style.display = 'block'; + correctionState[prop] = true; + } else { + reflineEls[reflineKey].style.display = 'none'; + } } - - return { x: newX, y: newY }; + return retVal; }, hide() { - xRefLine.style.display = 'none'; - yRefLine.style.display = 'none'; + Object.values(reflineEls).forEach((el) => (el.style.display = 'none')); }, destroy() { - xRefLine.remove(); - yRefLine.remove(); + lineWrap.remove(); }, }; }