Skip to content

Commit 5ad24ed

Browse files
authored
Merge pull request #456 from QAQCup/main
feat(av-cliper): add actable prop for VisibleSprite
2 parents 9035b2e + b6fb6aa commit 5ad24ed

File tree

4 files changed

+87
-17
lines changed

4 files changed

+87
-17
lines changed

packages/av-canvas/src/sprites/render-ctrl.ts

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@ import { CTRL_KEYS, TCtrlKey } from '../types';
33
import { createEl, getCvsRatio, getRectCtrls } from '../utils';
44
import { ESpriteManagerEvt, SpriteManager } from './sprite-manager';
55

6+
const CloseSvg = `
7+
<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">
8+
<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>
9+
</svg>
10+
`;
11+
612
export function renderCtrls(
713
container: HTMLElement,
814
cvsEl: HTMLCanvasElement,
@@ -43,7 +49,6 @@ export function renderCtrls(
4349
lastActSprEvtClear = s.on('propsChange', () => {
4450
syncCtrlElPos(s, cvsEl, rectEl, ctrlsEl);
4551
});
46-
rectEl.style.display = '';
4752
});
4853

4954
return () => {
@@ -76,7 +81,8 @@ function createRectAndCtrlEl(container: HTMLElement): {
7681
d.style.cssText = `
7782
display: none;
7883
position: absolute;
79-
border: 1px solid #3ee; border-radius: 50%;
84+
border: 1px solid #3ee;
85+
border-radius: 50%;
8086
box-sizing: border-box;
8187
background-color: #fff;
8288
pointer-events: auto;
@@ -112,14 +118,48 @@ function syncCtrlElPos(
112118
});
113119
Object.entries(getRectCtrls(cvsEl, s.rect)).forEach(([k, { x, y, w, h }]) => {
114120
// ctrl 是相对中心点定位的
115-
Object.assign(ctrlsEl[k as TCtrlKey].style, {
116-
display: 'block',
121+
const baseStyle = {
117122
left: '50%',
118123
top: '50%',
119124
width: `${w * cvsRatio.w}px`,
120125
height: `${h * cvsRatio.h}px`,
121-
// border 1px, 所以要 -1
122126
transform: `translate(${x * cvsRatio.w}px, ${y * cvsRatio.h}px)`,
123-
});
127+
};
128+
ctrlsEl[k as TCtrlKey].innerHTML = '';
129+
if (k === 'rotate') {
130+
Object.assign(ctrlsEl[k as TCtrlKey].style, {
131+
display: s.interactable === 'interactive' ? 'block' : 'none',
132+
...baseStyle,
133+
});
134+
} else {
135+
if (s.interactable === 'interactive') {
136+
Object.assign(ctrlsEl[k as TCtrlKey].style, {
137+
display: 'block',
138+
backgroundColor: '#fff',
139+
border: '1px solid #3ee',
140+
...baseStyle,
141+
});
142+
} else if (s.interactable === 'selectable') {
143+
Object.assign(ctrlsEl[k as TCtrlKey].style, {
144+
display: 'flex',
145+
justifyContent: 'center',
146+
alignItems: 'center',
147+
backgroundColor: 'transparent',
148+
border: 'none',
149+
...baseStyle,
150+
});
151+
ctrlsEl[k as TCtrlKey].innerHTML = CloseSvg;
152+
} else {
153+
Object.assign(ctrlsEl[k as TCtrlKey].style, {
154+
display: 'none',
155+
...baseStyle,
156+
});
157+
}
158+
}
124159
});
160+
if (s.interactable === 'disabled') {
161+
rectEl.style.display = 'none';
162+
} else {
163+
rectEl.style.display = '';
164+
}
125165
}

packages/av-canvas/src/sprites/sprite-manager.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export class SpriteManager {
2222
return this.#activeSprite;
2323
}
2424
set activeSprite(s: VisibleSprite | null) {
25-
if (s === this.#activeSprite) return;
25+
if (s === this.#activeSprite || s?.interactable === 'disabled') return;
2626
this.#activeSprite = s;
2727
this.#evtTool.emit(ESpriteManagerEvt.ActiveSpriteChange, s);
2828
}
@@ -32,7 +32,10 @@ export class SpriteManager {
3232
this.getSprites()
3333
// 排在后面的层级更高
3434
.reverse()
35-
.find((s) => s.visible && s.rect.checkHit(x, y)) ?? null;
35+
.find(
36+
(s) =>
37+
s.visible && s.interactable !== 'disabled' && s.rect.checkHit(x, y),
38+
) ?? null;
3639
}
3740

3841
async addSprite(vs: VisibleSprite): Promise<void> {

packages/av-canvas/src/sprites/sprite-op.ts

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,14 @@ export function draggabelSprite(
5151

5252
// 移动sprite的处理函数
5353
const onRectMouseDown = (evt: MouseEvent): void => {
54-
if (evt.button !== 0 || sprMng.activeSprite == null) return;
55-
5654
const hitSpr = sprMng.activeSprite;
55+
if (
56+
evt.button !== 0 ||
57+
hitSpr == null ||
58+
hitSpr.interactable !== 'interactive'
59+
)
60+
return;
61+
5762
const { clientX, clientY } = evt;
5863

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

7075
const cvsRatio = getCvsRatio(cvsEl);
7176
const onMouseMove = (evt: MouseEvent): void => {
72-
if (sprMng.activeSprite == null || startRect == null) return;
77+
const hitSpr = sprMng.activeSprite;
78+
if (
79+
hitSpr == null ||
80+
hitSpr.interactable !== 'interactive' ||
81+
startRect == null
82+
)
83+
return;
7384

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

7889
updateRectWithSafeMargin(
79-
sprMng.activeSprite.rect,
90+
hitSpr.rect,
8091
cvsEl,
81-
refline.magneticEffect(expectX, expectY, sprMng.activeSprite.rect),
92+
refline.magneticEffect(expectX, expectY, hitSpr.rect),
8293
);
8394
};
8495

@@ -116,18 +127,24 @@ function setupCtrlEvents(
116127
ctrlElements.forEach((ctrlEl, index) => {
117128
const ctrlKey = CTRL_KEYS[index];
118129
ctrlEl.addEventListener('pointerdown', (evt: MouseEvent) => {
119-
if (evt.button !== 0 || sprMng.activeSprite == null) return;
130+
const hitSpr = sprMng.activeSprite;
131+
if (
132+
evt.button !== 0 ||
133+
hitSpr == null ||
134+
hitSpr.interactable !== 'interactive'
135+
)
136+
return;
120137

121138
const { clientX, clientY } = evt;
122139

123140
if (ctrlKey === 'rotate') {
124141
rotateRect(
125-
sprMng.activeSprite.rect,
126-
cntMap2Outer(sprMng.activeSprite.rect.center, cvsRatio, cvsEl),
142+
hitSpr.rect,
143+
cntMap2Outer(hitSpr.rect.center, cvsRatio, cvsEl),
127144
);
128145
} else {
129146
scaleRect({
130-
sprRect: sprMng.activeSprite.rect,
147+
sprRect: hitSpr.rect,
131148
ctrlKey,
132149
startX: clientX,
133150
startY: clientY,

packages/av-cliper/src/sprite/visible-sprite.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,15 @@ export class VisibleSprite extends BaseSprite {
3030
*/
3131
visible = true;
3232

33+
/**
34+
* 控制 Sprite 的交互状态
35+
* - 'interactive': 可选中,可进行移动、缩放、旋转等所有交互
36+
* - 'selectable': 仅可选中,但不可进行移动、缩放、旋转等交互
37+
* - 'disabled': 不可选中,也不可交互
38+
* @default 'interactive'
39+
*/
40+
interactable: 'interactive' | 'selectable' | 'disabled' = 'interactive';
41+
3342
constructor(clip: IClip) {
3443
super();
3544
this.#clip = clip;
@@ -108,6 +117,7 @@ export class VisibleSprite extends BaseSprite {
108117
super.copyStateTo(target);
109118
if (target instanceof VisibleSprite) {
110119
target.visible = this.visible;
120+
target.interactable = this.interactable;
111121
}
112122
}
113123

0 commit comments

Comments
 (0)