summaryrefslogtreecommitdiff
path: root/static/src/slides.js
blob: e4f64065a1db9aede0cace6539c240539326f551 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
const Flickity = require("flickity")
import "flickity/dist/flickity.min.css"
import apiConnector from "./connector.js"

class Slides {
	constructor(slidesContainer) {
		/* previous selected index */
		this.selectedIndex = 0
		this.assetIndex = 0

		this.slidesContainer = slidesContainer

		/* append 11 cells to carousel */
		const carousel = this.slidesContainer.querySelector("#slideshow-carousel")
		const cellTemplate = this.slidesContainer.querySelector("#carousel-cell-template")
		this.cells = []
		for (let i = 0; i < 11; i++)
			carousel.appendChild(cellTemplate.content.cloneNode(true))

		/* initialize slides */
		this.flickity = new Flickity(carousel, {
			wrapAround: true,
			prevNextButtons: false,
			pageDots: false,
			resize: true,
			setGallerySize: false,
		})

		this.flickity.on("scroll", progress => { this.scroll(progress) })
		this.flickity.on("staticClick", (e, pointer, cellElement, cellIndex) => { this.staticClick(e, pointer, cellElement, cellIndex) })
		this.flickity.on("dragEnd", () => { this.seek() })
		this.initImages()

		/* initialize seek buttons */
		const seekPrevButton = this.slidesContainer.querySelector("#prev-slide")
		const seekNextButton = this.slidesContainer.querySelector("#next-slide")

		seekPrevButton.addEventListener("click", () => { this.flickity.previous() ; this.seek() })
		seekNextButton.addEventListener("click", () => { this.flickity.next()     ; this.seek() })

		/* initialize seek callback */
		apiConnector.seekCallbacks.push(c => { this.seekCallback() })

		/* initialize top controls */
		const assetDownloadButton = this.slidesContainer.querySelector("#download")

		assetDownloadButton.addEventListener("click", () => { apiConnector.assetDownload(apiConnector.currentAsset) })
	}

	seek() {
		// this is just like calculating movement in lazycachelist.py
		// gets the min of the absolute values and returns signed value
		const increment = [
			this.flickity.selectedIndex - this.selectedIndex,         // no list wrap
			this.flickity.selectedIndex - this.flickity.cells.length, // wrap backwards (0  -> -1)
			this.flickity.cells.length  - this.selectedIndex,         // wrap forwards  (-1 ->  0)
		].reduce((key, v) => Math.abs(v) < Math.abs(key) ? v : key)
		this.selectedIndex = this.flickity.selectedIndex
		this.assetIndex += increment
		apiConnector.seek(increment)
	}

	seekCallback() {
		let i
		if (this.assetIndex !== apiConnector.assetIndex) {
			this.assetIndex = apiConnector.assetIndex
			i = apiConnector.movement

			for (; i > 0; i--) this.flickity.next()
			for (; i < 0; i++) this.flickity.previous()
			this.selectedIndex = this.flickity.selectedIndex
		}

		// load new imgs
		// TODO need to make the 11 cells a constant somehow
		for (i = 0; i < this.flickity.cells.length; i++) {
			const x = (i + this.selectedIndex + 6) % this.flickity.cells.length
			const e = this.flickity.cells[x].element
			const img = e.firstElementChild
			img.src = apiConnector.assetPreviewSrc(apiConnector.assets[i])
		}
	}

	/* Flickity function for scrolling to ensure next and prev pics are always
	 * visible and to transition between states */
	scroll(progress) {
		const normalizedProgress = progress / (1 / (this.flickity.cells.length-1))
		const liveSelectedIndex = Math.round(normalizedProgress)
		const localizedProgress = normalizedProgress - liveSelectedIndex

		const prevSelectedCell = this.flickity.cells.at((liveSelectedIndex-1) % this.flickity.cells.length).element
		const liveSelectedCell = this.flickity.cells.at((liveSelectedIndex  ) % this.flickity.cells.length).element
		const nextSelectedCell = this.flickity.cells.at((liveSelectedIndex+1) % this.flickity.cells.length).element

		const prevSelectedImage = prevSelectedCell.firstElementChild
		const liveSelectedImage = liveSelectedCell.firstElementChild
		const nextSelectedImage = nextSelectedCell.firstElementChild

		const prevMargin = prevSelectedCell.clientWidth - prevSelectedImage.clientWidth
		const liveMargin = liveSelectedCell.clientWidth - liveSelectedImage.clientWidth
		const nextMargin = nextSelectedCell.clientWidth - nextSelectedImage.clientWidth

		liveSelectedImage.style.marginLeft = liveMargin / 2 + "px"
		nextSelectedImage.style.marginLeft = Math.max(0, Math.min(nextMargin, nextMargin * (localizedProgress))) + "px"
		prevSelectedImage.style.marginLeft = prevMargin - Math.max(0, Math.min(prevMargin, prevMargin * localizedProgress * -1)) + "px" // TODO clean this
	}

	/* jump to clicked on slide */
	staticClick(e, pointer, cellElement, cellIndex) {
		this.flickity.select(cellIndex)
		this.seek()
	}

	/* make sure images have correct margin when loaded since scroll function
	 * depends on them being loaded */
	positionImageStatic(img) {
		const i = parseInt(img.dataset.index)
		if (i == this.flickity.selectedIndex)
			img.style.marginLeft = (img.parentElement.clientWidth - img.clientWidth) / 2 + "px"
		else if ((i + 1) % this.flickity.cells.length == this.flickity.selectedIndex)
			img.style.marginLeft = img.parentElement.clientWidth - img.clientWidth + "px"
	}

	imageLoaded(e) { this.positionImageStatic(e.target) }
	initImages() {
		const imgs = this.slidesContainer.querySelectorAll("#slideshow-carousel img")
		for (let i = 0; i < imgs.length; i++) {
			const img = imgs[i]
			img.dataset.index = i
			img.addEventListener("load", e => { this.imageLoaded(e) })
			if (img.complete)
				this.positionImageStatic(img)
		}
	}
}

export default slidesContainer => { new Slides(slidesContainer); return true }