修复画布音频拖放与拖拽热区

素材库显式拖放不再叠加级联偏移,音频卡中心落在鼠标松手位置
将音频播放按钮热区收紧到中央,释放卡片主体用于选择与拖拽
补充模型、桥接和画布交互回归测试并同步音频设计文档
This commit is contained in:
2026-07-14 10:23:28 +08:00
parent 838d819c3d
commit f7f799df81
7 changed files with 87 additions and 5 deletions
@@ -169,7 +169,7 @@ describe('ImageCanvasEditorModel', () => {
});
});
it('creates a layer from an account asset at the requested screen point', () => {
it('creates a cascaded layer from an account asset near the requested screen point', () => {
const asset: EditorAsset = {
id: 'asset-1',
label: '角色草图',
@@ -210,6 +210,31 @@ describe('ImageCanvasEditorModel', () => {
expect(layer.y).toBe(12);
});
it('centers a dropped asset exactly at the requested screen point', () => {
const asset: EditorAsset = {
id: 'asset-drop',
label: '投放素材',
src: 'data:image/png;base64,drop',
width: 640,
height: 480,
folderId: 'project',
sourceKind: 'uploaded',
sourceType: 'uploaded',
persisted: true,
};
const layer = createLayerFromAsset(
asset,
3,
{ x: 20, y: 40, scale: 2 },
{ x: 420, y: 340 },
{ applyCascadeOffset: false },
);
expect(layer.x + layer.width / 2).toBe(200);
expect(layer.y + layer.height / 2).toBe(150);
});
it('preserves source resource ids from the persisted asset library', () => {
const library = normalizeAssetLibrary({
folders: [
@@ -138,6 +138,7 @@ export function createLayerFromAsset(
index: number,
viewport: CanvasViewport,
screenCenter: { x: number; y: number },
options: { applyCascadeOffset?: boolean } = {},
): CanvasLayer {
const { width, height } = resolveLayerResolutionSize(
asset.width,
@@ -151,7 +152,7 @@ export function createLayerFromAsset(
};
const worldCenterX = (safeScreenCenter.x - viewport.x) / safeScale;
const worldCenterY = (safeScreenCenter.y - viewport.y) / safeScale;
const offset = index * 34;
const offset = options.applyCascadeOffset === false ? 0 : index * 34;
const assetKind: CanvasAssetKind | undefined =
asset.mediaType === 'video'
? 'video'
@@ -934,6 +934,50 @@ describe('ImageCanvasWorldView', () => {
expect(props.onLayerClick).not.toHaveBeenCalled();
});
it('starts layer dragging from the audio card outside playback controls', () => {
const layer = createLayer({
title: '拖拽音效',
src: 'data:audio/mpeg;base64,audio',
mediaType: 'audio',
assetKind: 'sound-effect',
width: 420,
height: 120,
originalWidth: 420,
originalHeight: 120,
durationSeconds: 12,
});
const { props } = renderWorldView({
layers: [layer],
hoveredLayerId: layer.id,
});
const layerButton = screen.getByRole('button', { name: '选择拖拽音效' });
const audioCard = layerButton.querySelector(
'.image-canvas-editor__audio-card',
);
const playbackButton = within(layerButton).getByRole('button', {
name: '播放拖拽音效',
});
const controls = layerButton.querySelector(
'.image-canvas-editor__audio-card-controls',
);
expect(audioCard).toBeTruthy();
expect(controls).toBeTruthy();
fireEvent.pointerDown(audioCard as HTMLElement);
expect(props.onLayerPointerDown).toHaveBeenCalledWith(
expect.any(Object),
layer,
);
fireEvent.pointerDown(playbackButton);
fireEvent.pointerDown(controls as HTMLElement);
expect(props.onLayerPointerDown).toHaveBeenCalledTimes(1);
});
it('places layer type labels to the left of the information action', () => {
const layer = createLayer({ assetKind: 'character' });
const { props } = renderWorldView({ layers: [layer] });
@@ -134,7 +134,7 @@ function AssetCanvasBridgeHarness({
<div>
<button
type="button"
onClick={() => bridge.addAssetLayer(asset, { x: 450, y: 320 })}
onClick={() => bridge.addAssetLayer(asset)}
>
添加到画布
</button>
@@ -234,7 +234,11 @@ describe('useImageCanvasAssetCanvasBridge', () => {
});
expect(appendCanvasLayersWithResources).toHaveBeenCalledWith([
expect.objectContaining({ sourceAssetId: 'asset-1' }),
expect.objectContaining({
sourceAssetId: 'asset-1',
x: 75,
y: 190,
}),
]);
expect(screen.getByTestId('drag').textContent).toBe('none');
expect(screen.getByTestId('drop-target').textContent).toBe('-');
@@ -139,6 +139,7 @@ export function useImageCanvasAssetCanvasBridge({
x: position?.x ?? canvasSize.width / 2,
y: position?.y ?? canvasSize.height / 2,
},
{ applyCascadeOffset: position === undefined },
);
captureCanvasHistory();
appendCanvasLayersWithResources([nextLayer]);
+6 -1
View File
@@ -5714,14 +5714,19 @@ html[data-mobile-keyboard-open='true'] .platform-mobile-bottom-dock {
.image-canvas-editor__audio-card-playback {
position: absolute;
inset: 0;
left: 50%;
top: 50%;
z-index: 1;
display: grid;
width: 2.75rem;
height: 2.75rem;
place-items: center;
border: 0;
border-radius: 999px;
background: transparent;
color: rgba(255, 255, 255, 0.9);
cursor: pointer;
transform: translate(-50%, -50%);
}
.image-canvas-editor__audio-card-playback:hover {