Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 46 additions & 6 deletions packages/av-canvas/src/sprites/render-ctrl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ import { CTRL_KEYS, TCtrlKey } from '../types';
import { createEl, getCvsRatio, getRectCtrls } from '../utils';
import { ESpriteManagerEvt, SpriteManager } from './sprite-manager';

const CloseSvg = `
<svg t="1756779136804" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1456" width="16" height="16">
<path d="M1022.793875 170.063604L852.730271 0 511.396938 341.333333 170.063604 0 0 170.063604l341.333333 341.333334L0 852.730271l170.063604 170.063604 341.333334-340.127208 341.333333 340.127208 170.063604-170.063604-340.127208-341.333333 340.127208-341.333334z" fill="#bfbfbf" p-id="1457"></path>
</svg>
`;

export function renderCtrls(
container: HTMLElement,
cvsEl: HTMLCanvasElement,
Expand Down Expand Up @@ -43,7 +49,6 @@ export function renderCtrls(
lastActSprEvtClear = s.on('propsChange', () => {
syncCtrlElPos(s, cvsEl, rectEl, ctrlsEl);
});
rectEl.style.display = '';
});

return () => {
Expand Down Expand Up @@ -76,7 +81,8 @@ function createRectAndCtrlEl(container: HTMLElement): {
d.style.cssText = `
display: none;
position: absolute;
border: 1px solid #3ee; border-radius: 50%;
border: 1px solid #3ee;
border-radius: 50%;
box-sizing: border-box;
background-color: #fff;
pointer-events: auto;
Expand Down Expand Up @@ -112,14 +118,48 @@ function syncCtrlElPos(
});
Object.entries(getRectCtrls(cvsEl, s.rect)).forEach(([k, { x, y, w, h }]) => {
// ctrl 是相对中心点定位的
Object.assign(ctrlsEl[k as TCtrlKey].style, {
display: 'block',
const baseStyle = {
left: '50%',
top: '50%',
width: `${w * cvsRatio.w}px`,
height: `${h * cvsRatio.h}px`,
// border 1px, 所以要 -1
transform: `translate(${x * cvsRatio.w}px, ${y * cvsRatio.h}px)`,
});
};
ctrlsEl[k as TCtrlKey].innerHTML = '';
if (k === 'rotate') {
Object.assign(ctrlsEl[k as TCtrlKey].style, {
display: s.interactable === 'interactive' ? 'block' : 'none',
...baseStyle,
});
} else {
if (s.interactable === 'interactive') {
Object.assign(ctrlsEl[k as TCtrlKey].style, {
display: 'block',
backgroundColor: '#fff',
border: '1px solid #3ee',
...baseStyle,
});
} else if (s.interactable === 'selectable') {
Object.assign(ctrlsEl[k as TCtrlKey].style, {
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: 'transparent',
border: 'none',
...baseStyle,
});
ctrlsEl[k as TCtrlKey].innerHTML = CloseSvg;
} else {
Object.assign(ctrlsEl[k as TCtrlKey].style, {
display: 'none',
...baseStyle,
});
}
}
});
if (s.interactable === 'disabled') {
rectEl.style.display = 'none';
} else {
rectEl.style.display = '';
}
}
7 changes: 5 additions & 2 deletions packages/av-canvas/src/sprites/sprite-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ export class SpriteManager {
return this.#activeSprite;
}
set activeSprite(s: VisibleSprite | null) {
if (s === this.#activeSprite) return;
if (s === this.#activeSprite || s?.interactable === 'disabled') return;
this.#activeSprite = s;
this.#evtTool.emit(ESpriteManagerEvt.ActiveSpriteChange, s);
}
Expand All @@ -32,7 +32,10 @@ export class SpriteManager {
this.getSprites()
// 排在后面的层级更高
.reverse()
.find((s) => s.visible && s.rect.checkHit(x, y)) ?? null;
.find(
(s) =>
s.visible && s.interactable !== 'disabled' && s.rect.checkHit(x, y),
) ?? null;
}

async addSprite(vs: VisibleSprite): Promise<void> {
Expand Down
35 changes: 26 additions & 9 deletions packages/av-canvas/src/sprites/sprite-op.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,9 +51,14 @@ export function draggabelSprite(

// 移动sprite的处理函数
const onRectMouseDown = (evt: MouseEvent): void => {
if (evt.button !== 0 || sprMng.activeSprite == null) return;

const hitSpr = sprMng.activeSprite;
if (
evt.button !== 0 ||
hitSpr == null ||
hitSpr.interactable !== 'interactive'
)
return;

const { clientX, clientY } = evt;

startRect = hitSpr.rect.clone();
Expand All @@ -69,16 +74,22 @@ export function draggabelSprite(

const cvsRatio = getCvsRatio(cvsEl);
const onMouseMove = (evt: MouseEvent): void => {
if (sprMng.activeSprite == null || startRect == null) return;
const hitSpr = sprMng.activeSprite;
if (
hitSpr == null ||
hitSpr.interactable !== 'interactive' ||
startRect == null
)
return;

const { clientX, clientY } = evt;
let expectX = startRect.x + (clientX - startX) / cvsRatio.w;
let expectY = startRect.y + (clientY - startY) / cvsRatio.h;

updateRectWithSafeMargin(
sprMng.activeSprite.rect,
hitSpr.rect,
cvsEl,
refline.magneticEffect(expectX, expectY, sprMng.activeSprite.rect),
refline.magneticEffect(expectX, expectY, hitSpr.rect),
);
};

Expand Down Expand Up @@ -116,18 +127,24 @@ function setupCtrlEvents(
ctrlElements.forEach((ctrlEl, index) => {
const ctrlKey = CTRL_KEYS[index];
ctrlEl.addEventListener('pointerdown', (evt: MouseEvent) => {
if (evt.button !== 0 || sprMng.activeSprite == null) return;
const hitSpr = sprMng.activeSprite;
if (
evt.button !== 0 ||
hitSpr == null ||
hitSpr.interactable !== 'interactive'
)
return;

const { clientX, clientY } = evt;

if (ctrlKey === 'rotate') {
rotateRect(
sprMng.activeSprite.rect,
cntMap2Outer(sprMng.activeSprite.rect.center, cvsRatio, cvsEl),
hitSpr.rect,
cntMap2Outer(hitSpr.rect.center, cvsRatio, cvsEl),
);
} else {
scaleRect({
sprRect: sprMng.activeSprite.rect,
sprRect: hitSpr.rect,
ctrlKey,
startX: clientX,
startY: clientY,
Expand Down
10 changes: 10 additions & 0 deletions packages/av-cliper/src/sprite/visible-sprite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,15 @@ export class VisibleSprite extends BaseSprite {
*/
visible = true;

/**
* 控制 Sprite 的交互状态
* - 'interactive': 可选中,可进行移动、缩放、旋转等所有交互
* - 'selectable': 仅可选中,但不可进行移动、缩放、旋转等交互
* - 'disabled': 不可选中,也不可交互
* @default 'interactive'
*/
interactable: 'interactive' | 'selectable' | 'disabled' = 'interactive';

constructor(clip: IClip) {
super();
this.#clip = clip;
Expand Down Expand Up @@ -108,6 +117,7 @@ export class VisibleSprite extends BaseSprite {
super.copyStateTo(target);
if (target instanceof VisibleSprite) {
target.visible = this.visible;
target.interactable = this.interactable;
}
}

Expand Down
Loading