Skip to content

Commit

Permalink
make timeline mobile friendly, improve ui
Browse files Browse the repository at this point in the history
  • Loading branch information
zenkyuv committed Sep 5, 2024
1 parent 6c33db2 commit a8007d8
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 166 deletions.
43 changes: 22 additions & 21 deletions s/components/omni-timeline/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,42 +20,43 @@ export const OmniTimeline = shadow_component(use => {
use.styles(styles)
use.watch(() => use.context.state)
const state = use.context.state
const effect_drag = use.context.controllers.timeline.effect_drag
const playhead_drag = use.context.controllers.timeline.playhead_drag
const handler = use.context.controllers.timeline.effect_trim_handler
const effectTrim = use.context.controllers.timeline.effect_trim_handler
const effectDrag = use.context.controllers.timeline.effectDragHandler
const playheadDrag = use.context.controllers.timeline.playheadDragHandler

use.mount(() => {
window.addEventListener("dragover", augmented_dragover)
return () => removeEventListener("dragover", augmented_dragover)
window.addEventListener("pointermove", augmented_dragover)
return () => removeEventListener("pointermove", augmented_dragover)
})

const playhead_drag_over = (event: DragEvent) => {
const playheadDragOver = (event: PointerEvent) => {
const timeline = use.shadow.querySelector(".timeline-relative")
const bounds = timeline?.getBoundingClientRect()
const x = event.clientX - bounds!.left
if(x >= 0) {
playhead_drag.dropzone.dragover({x})(event)
} else playhead_drag.dropzone.dragover({x: 0})(event)
if(bounds) {
const x = event.clientX - bounds?.left
if(x >= 0) {
playheadDrag.move(x)
} else playheadDrag.move(0)
}
}

const effect_drag_over = (event: DragEvent) => {
const effect_drag_over = (event: PointerEvent) => {
const timeline = use.shadow.querySelector(".timeline-relative")
const indicator = (event.target as HTMLElement).part.value as Indicator
const bounds = timeline?.getBoundingClientRect()
const x = event.clientX - bounds!.left
const y = event.clientY - bounds!.top
effect_drag.dropzone.dragover({
coordinates: [x >= 0 ? x : 0, y >= 0 ? y : 0],
indicator: indicator,
})(event)
if(bounds) {
const x = event.clientX - bounds.left
const y = event.clientY - bounds.top
effectDrag.move({coordinates: [x >= 0 ? x : 0, y >= 0 ? y : 0], indicator})
}
}

function augmented_dragover(event: DragEvent) {
if(use.context.controllers.timeline.effect_trim_handler.effect_resize_handle_drag.grabbed) {
handler.effect_dragover(event.clientX, use.context.state)
function augmented_dragover(event: PointerEvent) {
if(effectTrim.grabbed) {
effectTrim.effect_dragover(event.clientX, use.context.state)
return
}
playhead_drag_over(event)
playheadDragOver(event)
effect_drag_over(event)
}

Expand Down
3 changes: 2 additions & 1 deletion s/components/omni-timeline/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ export const styles = css`
.timeline {
display: flex;
flex-direction: column;
user-select: none;
& .timeline-relative {
position: relative;
Expand Down
95 changes: 52 additions & 43 deletions s/components/omni-timeline/views/effects/parts/effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import {CSSResultGroup, TemplateResult, html, css, GoldElement} from "@benev/sla

import {styles} from "./styles.js"
import {V2} from "../../../utils/coordinates_in_rect.js"
import {AnyEffect} from "../../../../../context/types.js"
import {AnyEffect, At, Indicator} from "../../../../../context/types.js"
import {shadow_view} from "../../../../../context/context.js"
import {calculate_effect_width} from "../../../utils/calculate_effect_width.js"
import {calculate_start_position} from "../../../utils/calculate_start_position.js"
Expand All @@ -13,12 +13,11 @@ export const Effect = shadow_view(use => (timeline: GoldElement, any_effect: Any
use.watch(() => use.context.state)

const effect = use.context.state.effects.find(effect => effect.id === any_effect.id)!
const {effect_drag, on_drop} = use.context.controllers.timeline
const [[x, y], setCords] = use.state<V2 | [null, null]>([null, null])
const zoom = use.context.state.zoom
const {grabbed, hovering} = effect_drag
const controller = use.context.controllers.timeline
const media_controller = use.context.controllers.media
const effectDragHandler = use.context.controllers.timeline.effectDragHandler
const handler = controller.effect_trim_handler
const [fileNotFound, setFileNotFound] = use.state(false)
const [timelineScrollLeft, setTimelineScrollLeft] = use.state(0)
Expand All @@ -38,8 +37,8 @@ export const Effect = shadow_view(use => (timeline: GoldElement, any_effect: Any
}
}))

use.mount(() => on_drop(({grabbed}) => {
if(grabbed.effect.id === effect.id) {
use.mount(() => effectDragHandler.onDrop(({grabbed}) => {
if(grabbed?.effect?.id === effect?.id) {
setPreviewPosition({
startAtPosition: null,
start: null,
Expand Down Expand Up @@ -72,36 +71,40 @@ export const Effect = shadow_view(use => (timeline: GoldElement, any_effect: Any

const drag_events = {
effect_drag_listener() {
const effect_is_dragged = hovering?.coordinates && grabbed?.effect.id === effect.id
if(effect_is_dragged) {
const center_of_effect: V2 = [
hovering.coordinates[0] - grabbed.offset.x,
hovering.coordinates[1] - grabbed.offset.y
]
setCords(center_of_effect)
}
},
start(event: DragEvent) {
const img = new Image()
img.src = 'data:image/gif;base64,R0lGODlhAQABAIAAAAUEBAAAACwAAAAAAQABAAACAkQBADs='
event.dataTransfer?.setDragImage(img, 0, 0)
effect_drag.dragzone.dragstart({effect, offset: {x: event.offsetX, y: event.offsetY}})(event)
const dispose = effectDragHandler.onEffectDrag((e) => {
const isDragged = e.grabbed.effect.id === effect.id
if(isDragged) {
const center_of_effect: V2 = [
e.hovering.coordinates[0] - e.grabbed.offset.x,
e.hovering.coordinates[1] - e.grabbed.offset.y
]
setCords(center_of_effect)
}
})
return () => dispose()
},
drop(event: DragEvent) {
const hovering = use.context.controllers.timeline.effect_drag.hovering
if(hovering) {
effect_drag.dropzone.drop(hovering)(event)
} else {
effect_drag.dragzone.dragend()(event)
start(event: PointerEvent) {
const timelineElement = timeline.shadowRoot?.querySelector(".timeline-relative")
const bounds = timelineElement?.getBoundingClientRect()
const indicator = (event.target as HTMLElement).part.value as Indicator
if(bounds && !handler.grabbed) {
const x = event.clientX - bounds.left
const y = event.clientY - bounds.top
const at = {coordinates: [x >= 0 ? x : 0, y >= 0 ? y : 0], indicator} satisfies At
effectDragHandler.start({effect, offset: {x: event.offsetX, y: event.offsetY}}, at)
}
},
end(event: DragEvent) {
effect_drag.dragzone.dragend()(event)
drop() {
effectDragHandler.drop()
},
end() {
effectDragHandler.end()
}
}

use.mount(() => {
const dispose = handler.onDragOver(({start_at_position, effectId, start, end}) => {
const dispose = drag_events.effect_drag_listener()
const dispose1 = handler.onDragOver(({start_at_position, effectId, start, end}) => {
if(effectId === effect.id) {
setPreviewPosition({
start,
Expand All @@ -110,20 +113,25 @@ export const Effect = shadow_view(use => (timeline: GoldElement, any_effect: Any
})
}
})
window.addEventListener("dragend", drag_events.end)
window.addEventListener("drop", drag_events.drop)
return () => {removeEventListener("drop", drag_events.drop); removeEventListener("dragend", drag_events.end); dispose()}
const dropevents = (e: PointerEvent) => {
drag_events.drop()
handler.trim_drop(e, use.context.state)
}
const endevents = (e: PointerEvent) => {
drag_events.end()
handler.trim_end(e, use.context.state)
}
window.addEventListener("pointercancel", endevents)
window.addEventListener("pointerup", dropevents)
return () => {removeEventListener("pointerup", dropevents); removeEventListener("pointercancel", endevents); dispose(); dispose1()}
})

drag_events.effect_drag_listener()

const render_trim_handle = (side: "left" | "right") => {
return html`
<span
draggable="true"
@drop=${(e: DragEvent) => handler.trim_drop(e, use.context.state)}
@dragend=${(e: DragEvent) => handler.trim_end(e, use.context.state)}
@dragstart=${(e: DragEvent) => handler.trim_start(e, effect, side)}
@pointerup=${(e: PointerEvent) => handler.trim_drop(e, use.context.state)}
@pointercancel=${(e: PointerEvent) => handler.trim_end(e, use.context.state)}
@pointerdown=${(e: PointerEvent) => handler.trim_start(e, effect, side)}
class="trim-handle-${side}"
>
<span class=line></span>
Expand All @@ -132,21 +140,23 @@ export const Effect = shadow_view(use => (timeline: GoldElement, any_effect: Any
`
}

const grabbed = effectDragHandler.grabbed?.effect === effect

const renderPreview = () => {
return html`
<div
?data-grabbed=${grabbed?.effect === effect}
?data-grabbed=${grabbed}
?data-selected=${use.context.state.selected_effect?.id === effect.id}
style="
${inline_css}
background-image: none;
width: ${((previewPosition.end ?? effect.end) - (previewPosition.start ?? effect.start)) * Math.pow(2, zoom)}px;
transform: translate(
${x ?? calculate_start_position(previewPosition.startAtPosition ?? effect.start_at_position, use.context.state.zoom)}px,
${y ?? calculate_effect_track_placement(effect.track, use.context.state.effects)}px
);
"
@dragstart=${drag_events.start}
draggable="true"
@pointerdown=${drag_events.start}
class="trim-handles"
>
${render_trim_handle("left")}
Expand All @@ -160,7 +170,7 @@ export const Effect = shadow_view(use => (timeline: GoldElement, any_effect: Any
<span
class="effect"
?data-no-file=${fileNotFound}
?data-grabbed=${grabbed?.effect === effect}
?data-grabbed=${grabbed}
?data-selected=${use.context.state.selected_effect?.id === effect.id}
style="
${inline_css}
Expand All @@ -170,8 +180,7 @@ export const Effect = shadow_view(use => (timeline: GoldElement, any_effect: Any
${y ?? calculate_effect_track_placement(effect.track, use.context.state.effects)}px
);
"
draggable="true"
@dragstart=${drag_events.start}
@pointerdown=${drag_events.start}
@click=${() => use.context.controllers.timeline.set_selected_effect(effect, use.context.controllers.compositor, use.context.state)}
>
${fileNotFound
Expand Down
1 change: 1 addition & 0 deletions s/components/omni-timeline/views/effects/parts/styles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export const styles = css`
z-index: 5;
mix-blend-mode: overlay;
background: rgb(255,255,255,0.8);
touch-action: none;
& .trim-handle-right, .trim-handle-left {
filter: drop-shadow(2px 4px 6px black);
Expand Down
1 change: 1 addition & 0 deletions s/components/omni-timeline/views/effects/video-effect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ export const VideoEffect = shadow_view(use => (effect: XVideoEffect, timeline: G

return html`${Effect([timeline, effect, html`${render_filmstrip()}`, css`
.content {
pointer-events: none;
width: 100%;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,21 @@ import {html} from "@benev/slate"
import {light_view} from "../../../../context/context.js"

export const AddTrackIndicator = light_view(use => () => {
const drag = use.context.controllers.timeline.effect_drag
const drag = use.context.controllers.timeline.effectDragHandler
const [indicator, setIndicator] = use.state(false)

const drag_events = {
drop(e: DragEvent) {
if(drag.hovering) {
drag.dropzone.drop(drag.hovering)(e)
if(drag.grabbed) {
// drag.dropzone.drop(drag.hovering)(e)
setIndicator(false)
} else {
setIndicator(false)
drag.dragzone.dragend()(e)
// drag.dragzone.dragend()(e)
}
},
end(e: DragEvent) {
drag.dragzone.dragend()(e)
// drag.dragzone.dragend()(e)
setIndicator(false)
},
enter: () => setIndicator(true),
Expand All @@ -31,11 +31,11 @@ export const AddTrackIndicator = light_view(use => () => {
>
</div>
<div
@pointerenter=${drag_events.enter}
@pointerleave=${drag_events.leave}
@pointerup=${drag_events.drop}
@pointercancel=${drag_events.end}
data-indicator="add-track"
@dragenter=${drag_events.enter}
@dragleave=${drag_events.leave}
@drop=${drag_events.drop}
@dragend=${drag_events.end}
class="indicator-area"
>
</div>
Expand Down
51 changes: 31 additions & 20 deletions s/components/omni-timeline/views/indicators/proposal-indicator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export const ProposalIndicator = light_view(use => () => {
const actions = use.context.actions
const zoom = use.context.state.zoom
const trim_handler = use.context.controllers.timeline.effect_trim_handler
const {effect_drag: {hovering, grabbed}, on_drop} = controller
const [proposedTimecode, setProposedTimecode, getProposedTimecode] = use.state<ProposedTimecode>({
const effectDragHandler = controller.effectDragHandler
const [proposedTimecode, setProposedTimecode] = use.state<ProposedTimecode>({
proposed_place: {track: 0, start_at_position: 0},
duration: null,
effects_to_push: null
Expand All @@ -38,22 +38,33 @@ export const ProposalIndicator = light_view(use => () => {
}
}

use.mount(() => on_drop(({grabbed, dropped_at}) => {
const proposed_timecode = getProposedTimecode()
controller.set_proposed_timecode(grabbed.effect, proposed_timecode)
if(dropped_at.indicator === "add-track-indicator") {actions.add_track()}
}))
use.mount(() => {
const disposeDrop = effectDragHandler.onDrop(({grabbed, droppedAt}) => {
const timecode = translate_to_timecode(grabbed, droppedAt)
const proposed_timecode = use.context.controllers.timeline
.calculate_proposed_timecode(
timecode,
grabbed!.effect.id,
use.context.state
)
controller.set_proposed_timecode(grabbed.effect, proposed_timecode)
if(droppedAt && droppedAt.indicator === "add-track-indicator") {actions.add_track()}
})

if(hovering && grabbed) {
const timecode = translate_to_timecode(grabbed, hovering)
const proposed_timecode = use.context.controllers.timeline
.calculate_proposed_timecode(
timecode,
grabbed!.effect.id,
use.context.state
)
setProposedTimecode(proposed_timecode)
}
const disposeDrag = effectDragHandler.onEffectDrag(({grabbed, hovering}) => {
if(hovering && grabbed) {
const timecode = translate_to_timecode(grabbed, hovering)
const proposed_timecode = use.context.controllers.timeline
.calculate_proposed_timecode(
timecode,
grabbed!.effect.id,
use.context.state
)
setProposedTimecode(proposed_timecode)
}
})
return () => {disposeDrag(); disposeDrop()}
})

const text_effect_specific_styles = () => {
return !track_effects.some(effect => effect.kind !== "text") && track_effects.find(effect => effect.kind === "text")
Expand All @@ -66,14 +77,14 @@ export const ProposalIndicator = light_view(use => () => {
?data-push-effects=${proposedTimecode?.effects_to_push}
style="
${text_effect_specific_styles()}
display: ${grabbed && !trim_handler.effect_resize_handle_drag.grabbed ? "block" : "none"};
display: ${effectDragHandler.grabbed && !trim_handler.grabbed ? "block" : "none"};
width: ${
proposedTimecode.effects_to_push
? 0
: proposedTimecode.duration
? proposedTimecode.duration * Math.pow(2, zoom)
: grabbed
? calculate_effect_width(grabbed.effect, zoom)
: effectDragHandler.grabbed
? calculate_effect_width(effectDragHandler.grabbed.effect, zoom)
: 0
}px;
transform: translate(
Expand Down
Loading

0 comments on commit a8007d8

Please sign in to comment.