Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[project-s] ドラッグ周りの整理とプレビュー処理の追加 #1680

Merged
merged 13 commits into from
Dec 28, 2023
Merged
650 changes: 383 additions & 267 deletions src/components/Sing/ScoreSequencer.vue

Large diffs are not rendered by default.

98 changes: 32 additions & 66 deletions src/components/Sing/SequencerNote.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<template>
<div
:class="classNamesStr"
:class="classNames"
:style="{
transform: `translate3d(${positionX}px,${positionY}px,0)`,
}"
Expand All @@ -17,38 +17,37 @@
:width="barWidth"
xmlns="http://www.w3.org/2000/svg"
class="sequencer-note-bar"
focusable="true"
tabindex="0"
@dblclick.prevent.stop="removeNote"
@keydown.prevent="handleKeydown"
>
<g>
<rect
y="0"
x="0"
height="100%"
width="100%"
rx="2"
ry="2"
stroke-width="1"
class="sequencer-note-bar-body"
@mousedown.stop="handleMouseDown"
:class="{ 'cursor-move': !isPreview }"
@mousedown="onBodyMouseDown"
/>
<rect
y="-25%"
x="-4"
height="150%"
width="12"
fill-opacity="0"
class="sequencer-note-bar-draghandle"
@mousedown.stop="handleDragLeftStart"
:class="{ 'cursor-ew-resize': !isPreview }"
@mousedown="onLeftEdgeMouseDown"
/>
<rect
y="-25%"
:x="barWidth - 4"
:x="barWidth - 8"
width="12"
height="150%"
fill-opacity="0"
class="sequencer-note-bar-draghandle"
@mousedown.stop="handleDragRightStart"
:class="{ 'cursor-ew-resize': !isPreview }"
@mousedown="onRightEdgeMouseDown"
/>
</g>
</svg>
Expand All @@ -63,25 +62,16 @@ import {
getKeyBaseHeight,
tickToBaseX,
noteNumberToBaseY,
PREVIEW_SOUND_DURATION,
} from "@/helpers/singHelper";

export default defineComponent({
name: "SingSequencerNote",
props: {
note: { type: Object as PropType<Note>, required: true },
index: { type: Number, required: true },
cursorX: { type: Number },
cursorY: { type: Number },
isSelected: { type: Boolean },
isPreview: { type: Boolean },
},

emits: [
"handleNotesKeydown",
"handleDragMoveStart",
"handleDragRightStart",
"handleDragLeftStart",
],

emits: ["bodyMousedown", "rightEdgeMousedown", "leftEdgeMousedown"],
setup(props, { emit }) {
const store = useStore();
const state = store.state;
Expand All @@ -104,20 +94,16 @@ export default defineComponent({
const noteEndBaseX = tickToBaseX(noteEndTicks, tpqn.value);
return (noteEndBaseX - noteStartBaseX) * zoomX.value;
});
const classNamesStr = computed(() => {
if (state.selectedNoteIds.includes(props.note.id)) {
const classNames = computed(() => {
if (props.isSelected) {
return "sequencer-note selected";
}
if (state.overlappingNoteIds.includes(props.note.id)) {
if (state.overlappingNoteIds.has(props.note.id)) {
return "sequencer-note overlapping";
}
return "sequencer-note";
});

const removeNote = () => {
store.dispatch("REMOVE_NOTES", { noteIds: [props.note.id] });
};

const setLyric = (event: Event) => {
if (!(event.target instanceof HTMLInputElement)) {
return;
Expand All @@ -128,38 +114,16 @@ export default defineComponent({
}
};

const selectThisNote = () => {
store.dispatch("SELECT_NOTES", { noteIds: [props.note.id] });
store.dispatch("PLAY_PREVIEW_SOUND", {
noteNumber: props.note.noteNumber,
duration: PREVIEW_SOUND_DURATION,
});
};

const handleKeydown = (event: KeyboardEvent) => {
emit("handleNotesKeydown", event);
};

const handleMouseDown = (event: MouseEvent) => {
if (!state.selectedNoteIds.includes(props.note.id)) {
selectThisNote();
} else {
emit("handleDragMoveStart", event);
}
const onBodyMouseDown = (event: MouseEvent) => {
emit("bodyMousedown", event);
};

const handleDragRightStart = (event: MouseEvent) => {
if (!state.selectedNoteIds.includes(props.note.id)) {
selectThisNote();
}
emit("handleDragRightStart", event);
const onRightEdgeMouseDown = (event: MouseEvent) => {
emit("rightEdgeMousedown", event);
};

const handleDragLeftStart = (event: MouseEvent) => {
if (!state.selectedNoteIds.includes(props.note.id)) {
selectThisNote();
}
emit("handleDragLeftStart", event);
const onLeftEdgeMouseDown = (event: MouseEvent) => {
emit("leftEdgeMousedown", event);
};

return {
Expand All @@ -169,13 +133,11 @@ export default defineComponent({
positionY,
barHeight,
barWidth,
classNamesStr,
removeNote,
classNames,
setLyric,
handleKeydown,
handleDragRightStart,
handleDragLeftStart,
handleMouseDown,
onBodyMouseDown,
onRightEdgeMouseDown,
onLeftEdgeMouseDown,
};
},
});
Expand All @@ -195,7 +157,6 @@ export default defineComponent({
&.selected {
.sequencer-note-bar-body {
fill: darkorange; // 仮
cursor: move;
}
}

Expand Down Expand Up @@ -227,6 +188,7 @@ export default defineComponent({
display: block;
position: relative;
}

.sequencer-note-bar-body {
fill: colors.$primary;
stroke: #fff;
Expand All @@ -236,7 +198,11 @@ export default defineComponent({
left: 0;
}

.sequencer-note-bar-draghandle {
.cursor-move {
cursor: move;
}

.cursor-ew-resize {
cursor: ew-resize;
}
</style>
2 changes: 2 additions & 0 deletions src/components/Sing/SequencerRuler.vue
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ export default defineComponent({
});

const onClick = (event: MouseEvent) => {
store.dispatch("DESELECT_ALL_NOTES");

const sequencerRulerElement = sequencerRuler.value;
if (!sequencerRulerElement) {
throw new Error("sequencerRulerElement is null.");
Expand Down
4 changes: 2 additions & 2 deletions src/components/Sing/ToolBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
icon="stop"
@click="stop"
></q-btn>
<q-btn flat round class="sing-transport-button" icon="loop"></q-btn>
<div class="sing-playhead-position">{{ playheadPositionStr }}</div>
<q-input
type="number"
Expand Down Expand Up @@ -382,7 +381,8 @@ export default defineComponent({

.sing-playhead-position {
font-size: 18px;
margin: 0 4px;
margin-left: 14px;
margin-right: 4px;
min-width: 82px;
}

Expand Down
14 changes: 7 additions & 7 deletions src/helpers/singHelper.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { Note, Tempo, TimeSignature } from "@/store/type";

export const BEAT_TYPES = [2, 4, 8, 16];
export const MAX_SNAP_TYPE = 32;

export const DEFAULT_TPQN = 480;
export const DEFAULT_BPM = 120;
export const DEFAULT_BEATS = 4;
export const DEFAULT_BEAT_TYPE = 4;

const BASE_X_PER_QUARTER_NOTE = 120;
const BASE_Y_PER_NOTE_NUMBER = 30;
const BASE_Y_PER_SEMITONE = 30;

export const ZOOM_X_MIN = 0.2;
export const ZOOM_X_MAX = 1;
Expand Down Expand Up @@ -115,7 +116,7 @@ export function isTriplet(noteType: number) {
}

export function getKeyBaseHeight() {
return BASE_Y_PER_NOTE_NUMBER;
return BASE_Y_PER_SEMITONE;
}

export function tickToBaseX(ticks: number, tpqn: number) {
Expand All @@ -128,21 +129,20 @@ export function baseXToTick(baseX: number, tpqn: number) {

// NOTE: ノート番号が整数のときに、そのノート番号のキーの中央の位置を返します
export function noteNumberToBaseY(noteNumber: number) {
return (127.5 - noteNumber) * BASE_Y_PER_NOTE_NUMBER;
return (127.5 - noteNumber) * BASE_Y_PER_SEMITONE;
}

// NOTE: integerがfalseの場合は、ノート番号のキーの中央の位置が
// ちょうどそのノート番号となるように計算します
export function baseYToNoteNumber(baseY: number, integer = true) {
return integer
? 127 - Math.floor(baseY / BASE_Y_PER_NOTE_NUMBER)
: 127.5 - baseY / BASE_Y_PER_NOTE_NUMBER;
? 127 - Math.floor(baseY / BASE_Y_PER_SEMITONE)
: 127.5 - baseY / BASE_Y_PER_SEMITONE;
}

export function getSnapTypes(tpqn: number) {
const maxSnapType = 64;
return getRepresentableNoteTypes(tpqn).filter((value) => {
return value <= maxSnapType;
return value <= MAX_SNAP_TYPE;
});
}

Expand Down
Loading
Loading