Skip to content

Commit

Permalink
Refactor: [Regions] virtualize DOM elements (#3712)
Browse files Browse the repository at this point in the history
* Refactor: [Regions] virtualize DOM elements

* Fix tests

* Virtual Timeline elements
  • Loading branch information
katspaugh authored May 18, 2024
1 parent 0d0a08e commit 26482e2
Show file tree
Hide file tree
Showing 5 changed files with 83 additions and 20 deletions.
36 changes: 19 additions & 17 deletions cypress/e2e/regions.cy.js
Original file line number Diff line number Diff line change
Expand Up @@ -77,24 +77,26 @@ describe('WaveSurfer Regions plugin tests', () => {

expect(region.start).to.equal(3)

// Drag the region
const pointerDownEvent = new PointerEvent('pointerdown', {
clientX: 90,
clientY: 1,
})
const pointerMoveEvent = new PointerEvent('pointermove', {
clientX: 200,
clientY: 10,
})
const pointerUpEvent = new PointerEvent('pointerup', {
clientX: 200,
clientY: 10,
})
region.element.dispatchEvent(pointerDownEvent)
win.document.dispatchEvent(pointerMoveEvent)
win.document.dispatchEvent(pointerUpEvent)
return cy.wait(10).then(() => {
// Drag the region
const pointerDownEvent = new PointerEvent('pointerdown', {
clientX: 90,
clientY: 1,
})
const pointerMoveEvent = new PointerEvent('pointermove', {
clientX: 200,
clientY: 10,
})
const pointerUpEvent = new PointerEvent('pointerup', {
clientX: 200,
clientY: 10,
})
region.element.dispatchEvent(pointerDownEvent)
win.document.dispatchEvent(pointerMoveEvent)
win.document.dispatchEvent(pointerUpEvent)

expect(region.start).to.be.greaterThan(3)
expect(region.start).to.be.greaterThan(3)
})
})
})

Expand Down
31 changes: 30 additions & 1 deletion src/plugins/regions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -505,8 +505,37 @@ class RegionsPlugin extends BasePlugin<RegionsPluginEvents, RegionsPluginOptions
}
}

private virtualAppend(region: Region, container: HTMLElement, element: HTMLElement) {
const renderIfVisible = () => {
if (!this.wavesurfer) return
const clientWidth = this.wavesurfer.getWidth()
const scrollLeft = this.wavesurfer.getScroll()
const scrollWidth = container.clientWidth
const duration = this.wavesurfer.getDuration()
const start = Math.round((region.start / duration) * scrollWidth)
const width = Math.round(((region.end - region.start) / duration) * scrollWidth) || 1

// Check if the region is between the scrollLeft and scrollLeft + clientWidth
const isVisible = start + width > scrollLeft && start < scrollLeft + clientWidth

if (isVisible) {
container.appendChild(element)
} else {
element.remove()
}
}

setTimeout(() => {
if (!this.wavesurfer) return
renderIfVisible()

const unsubscribe = this.wavesurfer.on('scroll', renderIfVisible)
this.subscriptions.push(region.once('remove', unsubscribe), unsubscribe)
}, 0)
}

private saveRegion(region: Region) {
this.regionsContainer.appendChild(region.element)
this.virtualAppend(region, this.regionsContainer, region.element)
this.avoidOverlapping(region)
this.regions.push(region)

Expand Down
27 changes: 25 additions & 2 deletions src/plugins/timeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,28 @@ class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOpti
return 2
}

private virtualAppend(start: number, container: HTMLElement, element: HTMLElement) {
const renderIfVisible = () => {
if (!this.wavesurfer) return
const clientWidth = this.wavesurfer.getWidth()
const scrollLeft = this.wavesurfer.getScroll()
const width = element.clientWidth
const isVisible = start + width > scrollLeft && start < scrollLeft + clientWidth

if (isVisible) {
container.appendChild(element)
} else {
element.remove()
}
}

setTimeout(() => {
if (!this.wavesurfer) return
renderIfVisible()
this.subscriptions.push(this.wavesurfer.on('scroll', renderIfVisible))
}, 0)
}

private initTimeline() {
const duration = this.wavesurfer?.getDuration() ?? this.options.duration ?? 0
const pxPerSec = this.timelineWrapper.scrollWidth / duration
Expand Down Expand Up @@ -217,8 +239,9 @@ class TimelinePlugin extends BasePlugin<TimelinePluginEvents, TimelinePluginOpti
const mode = isPrimary ? 'primary' : isSecondary ? 'secondary' : 'tick'
notch.setAttribute('part', `timeline-notch timeline-notch-${mode}`)

notch.style.left = `${i * pxPerSec}px`
timeline.appendChild(notch)
const offset = i * pxPerSec
notch.style.left = `${offset}px`
this.virtualAppend(offset, timeline, notch)
}

this.timelineWrapper.innerHTML = ''
Expand Down
4 changes: 4 additions & 0 deletions src/renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,10 @@ class Renderer extends EventEmitter<RendererEvents> {
return this.wrapper
}

getWidth(): number {
return this.scrollContainer.clientWidth
}

getScroll(): number {
return this.scrollContainer.scrollLeft
}
Expand Down
5 changes: 5 additions & 0 deletions src/wavesurfer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -380,6 +380,11 @@ class WaveSurfer extends Player<WaveSurferEvents> {
return this.renderer.getWrapper()
}

/** For plugins only: get the scroll container client width */
public getWidth(): number {
return this.renderer.getWidth()
}

/** Get the current scroll position in pixels */
public getScroll(): number {
return this.renderer.getScroll()
Expand Down

0 comments on commit 26482e2

Please sign in to comment.