dev@347
|
1 /**
|
dev@347
|
2 * Created by lucast on 24/05/2017.
|
dev@347
|
3 */
|
dev@347
|
4 import wavesUI from 'waves-ui-piper';
|
dev@347
|
5 import * as Hammer from 'hammerjs';
|
dev@347
|
6 import {TimePixelMapper} from '../playhead/PlayHeadHelpers';
|
dev@347
|
7
|
dev@347
|
8 // TODO this is named as such as a reminder that it needs to be re-factored
|
dev@347
|
9 export function attachTouchHandlerBodges(element: HTMLElement,
|
dev@347
|
10 timeline: Timeline) {
|
dev@347
|
11 interface Point {
|
dev@347
|
12 x: number;
|
dev@347
|
13 y: number;
|
dev@347
|
14 }
|
dev@347
|
15
|
dev@347
|
16 let zoomGestureJustEnded = false;
|
dev@347
|
17
|
dev@347
|
18 const pixelToExponent: Function = wavesUI.utils.scales.linear()
|
dev@347
|
19 .domain([0, 100]) // 100px => factor 2
|
dev@347
|
20 .range([0, 1]);
|
dev@347
|
21
|
dev@347
|
22 const calculateDistance: (p1: Point, p2: Point) => number = (p1, p2) => {
|
dev@347
|
23 return Math.pow(
|
dev@347
|
24 Math.pow(p2.x - p1.x, 2) +
|
dev@347
|
25 Math.pow(p2.y - p1.y, 2), 0.5);
|
dev@347
|
26 };
|
dev@347
|
27
|
dev@347
|
28 const calculateMidPoint: (p1: Point, p2: Point) => Point = (p1, p2) => {
|
dev@347
|
29 return {
|
dev@347
|
30 x: 0.5 * (p1.x + p2.x),
|
dev@347
|
31 y: 0.5 * (p1.y + p2.y)
|
dev@347
|
32 };
|
dev@347
|
33 };
|
dev@347
|
34
|
dev@347
|
35 const hammertime = new Hammer.Manager(element, {
|
dev@347
|
36 recognizers: [
|
dev@347
|
37 [Hammer.Pan, { direction: Hammer.DIRECTION_HORIZONTAL }]
|
dev@347
|
38 ]
|
dev@347
|
39 });
|
dev@347
|
40
|
dev@347
|
41 // it seems HammerJs binds the event to the window?
|
dev@347
|
42 // causing these events to propagate to other components?
|
dev@347
|
43 let initialZoom;
|
dev@347
|
44 let initialDistance;
|
dev@347
|
45 let offsetAtPanStart;
|
dev@347
|
46 let startX;
|
dev@347
|
47 let isZooming;
|
dev@347
|
48
|
dev@347
|
49 const scroll = (ev) => {
|
dev@347
|
50 if (ev.center.x - startX === 0) {
|
dev@347
|
51 return;
|
dev@347
|
52 }
|
dev@347
|
53
|
dev@347
|
54 if (zoomGestureJustEnded) {
|
dev@347
|
55 zoomGestureJustEnded = false;
|
dev@347
|
56 console.log('Skip this event: likely a single touch dangling from pinch');
|
dev@347
|
57 return;
|
dev@347
|
58 }
|
dev@347
|
59 timeline.timeContext.offset = offsetAtPanStart +
|
dev@347
|
60 timeline.timeContext.timeToPixel.invert(ev.deltaX);
|
dev@347
|
61 timeline.tracks.update();
|
dev@347
|
62 };
|
dev@347
|
63
|
dev@347
|
64 const zoom = (ev) => {
|
dev@347
|
65 if (ev.touches.length < 2) {
|
dev@347
|
66 return;
|
dev@347
|
67 }
|
dev@347
|
68
|
dev@347
|
69 ev.preventDefault();
|
dev@347
|
70 const minZoom = timeline.state.minZoom;
|
dev@347
|
71 const maxZoom = timeline.state.maxZoom;
|
dev@347
|
72 const p1: Point = {
|
dev@347
|
73 x: ev.touches[0].clientX,
|
dev@347
|
74 y: ev.touches[0].clientY
|
dev@347
|
75 };
|
dev@347
|
76 const p2: Point = {
|
dev@347
|
77 x: ev.touches[1].clientX,
|
dev@347
|
78 y: ev.touches[1].clientY
|
dev@347
|
79 };
|
dev@347
|
80 const distance = calculateDistance(p1, p2);
|
dev@347
|
81 const midPoint = calculateMidPoint(p1, p2);
|
dev@347
|
82
|
dev@347
|
83 const lastCenterTime =
|
dev@347
|
84 timeline.timeContext.timeToPixel.invert(midPoint.x);
|
dev@347
|
85
|
dev@347
|
86 const exponent = pixelToExponent(distance - initialDistance);
|
dev@347
|
87 const targetZoom = initialZoom * Math.pow(2, exponent);
|
dev@347
|
88
|
dev@347
|
89 timeline.timeContext.zoom =
|
dev@347
|
90 Math.min(Math.max(targetZoom, minZoom), maxZoom);
|
dev@347
|
91
|
dev@347
|
92 const newCenterTime =
|
dev@347
|
93 timeline.timeContext.timeToPixel.invert(midPoint.x);
|
dev@347
|
94
|
dev@347
|
95 timeline.timeContext.offset += newCenterTime - lastCenterTime;
|
dev@347
|
96 timeline.tracks.update();
|
dev@347
|
97 };
|
dev@347
|
98 hammertime.on('panstart', (ev) => {
|
dev@347
|
99 offsetAtPanStart = timeline.timeContext.offset;
|
dev@347
|
100 startX = ev.center.x;
|
dev@347
|
101 });
|
dev@347
|
102 hammertime.on('panleft', scroll);
|
dev@347
|
103 hammertime.on('panright', scroll);
|
dev@347
|
104
|
dev@347
|
105 element.addEventListener('touchstart', (e) => {
|
dev@347
|
106 if (e.touches.length < 2) {
|
dev@347
|
107 return;
|
dev@347
|
108 }
|
dev@347
|
109
|
dev@347
|
110 isZooming = true;
|
dev@347
|
111 initialZoom = timeline.timeContext.zoom;
|
dev@347
|
112
|
dev@347
|
113 initialDistance = calculateDistance({
|
dev@347
|
114 x: e.touches[0].clientX,
|
dev@347
|
115 y: e.touches[0].clientY
|
dev@347
|
116 }, {
|
dev@347
|
117 x: e.touches[1].clientX,
|
dev@347
|
118 y: e.touches[1].clientY
|
dev@347
|
119 });
|
dev@347
|
120 });
|
dev@347
|
121 element.addEventListener('touchend', () => {
|
dev@347
|
122 if (isZooming) {
|
dev@347
|
123 isZooming = false;
|
dev@347
|
124 zoomGestureJustEnded = true;
|
dev@347
|
125 }
|
dev@347
|
126 });
|
dev@347
|
127 element.addEventListener('touchmove', zoom);
|
dev@347
|
128 }
|
dev@347
|
129
|
dev@347
|
130 export function naivePagingMapper(timeline: Timeline): TimePixelMapper {
|
dev@347
|
131 return (currentTime: number) => {
|
dev@347
|
132 const currentOffset = timeline.timeContext.offset;
|
dev@347
|
133 const offsetTimestamp = currentOffset
|
dev@347
|
134 + currentTime;
|
dev@347
|
135
|
dev@347
|
136 const visibleDuration = timeline.timeContext.visibleDuration;
|
dev@347
|
137 const mustPageForward = offsetTimestamp > visibleDuration;
|
dev@347
|
138 const mustPageBackward = currentTime < -currentOffset;
|
dev@347
|
139
|
dev@347
|
140 if (mustPageForward) {
|
dev@347
|
141 const hasSkippedMultiplePages =
|
dev@347
|
142 offsetTimestamp - visibleDuration > visibleDuration;
|
dev@347
|
143
|
dev@347
|
144 timeline.timeContext.offset = hasSkippedMultiplePages ?
|
dev@347
|
145 -currentTime + 0.5 * visibleDuration :
|
dev@347
|
146 currentOffset - visibleDuration;
|
dev@347
|
147 }
|
dev@347
|
148
|
dev@347
|
149 if (mustPageBackward) {
|
dev@347
|
150 const hasSkippedMultiplePages =
|
dev@347
|
151 currentTime + visibleDuration < -currentOffset;
|
dev@347
|
152 timeline.timeContext.offset = hasSkippedMultiplePages ?
|
dev@347
|
153 -currentTime + 0.5 * visibleDuration :
|
dev@347
|
154 currentOffset + visibleDuration;
|
dev@347
|
155 }
|
dev@347
|
156
|
dev@347
|
157 if (mustPageForward || mustPageBackward) {
|
dev@347
|
158 timeline.tracks.update();
|
dev@347
|
159 }
|
dev@347
|
160 //
|
dev@347
|
161 return timeline.timeContext.timeToPixel(timeline.offset + currentTime);
|
dev@347
|
162 };
|
dev@347
|
163 }
|