WIP - Remove canvas and use HTML/CSS
Browse files- index.html +3 -0
- src/core/element.js +13 -0
- src/core/overlay.js +21 -151
- src/driver.scss +29 -0
- src/index.js +0 -16
- webpack.config.dev.js +10 -10
index.html
CHANGED
@@ -320,6 +320,9 @@ activeElement.getNode(); // Gets the DOM Element behind this element
|
|
320 |
</div>
|
321 |
</div>
|
322 |
|
|
|
|
|
|
|
323 |
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
|
324 |
<script async defer src="//buttons.github.io/buttons.js"></script>
|
325 |
<script src="//twemoji.maxcdn.com/2/twemoji.min.js?2.5"></script>
|
|
|
320 |
</div>
|
321 |
</div>
|
322 |
|
323 |
+
<div id="driver-page-overlay"></div>
|
324 |
+
<div id="driver-highlighted-element-stage"></div>
|
325 |
+
|
326 |
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
|
327 |
<script async defer src="//buttons.github.io/buttons.js"></script>
|
328 |
<script src="//twemoji.maxcdn.com/2/twemoji.min.js?2.5"></script>
|
src/core/element.js
CHANGED
@@ -138,6 +138,9 @@ export default class Element {
|
|
138 |
*/
|
139 |
onDeselected() {
|
140 |
this.hidePopover();
|
|
|
|
|
|
|
141 |
this.highlightFinished = false;
|
142 |
|
143 |
if (this.options.onDeselected) {
|
@@ -145,6 +148,14 @@ export default class Element {
|
|
145 |
}
|
146 |
}
|
147 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
148 |
/**
|
149 |
* Is called when the element is about to be highlighted
|
150 |
* i.e. either if overlay has started moving the highlight towards
|
@@ -168,6 +179,8 @@ export default class Element {
|
|
168 |
onHighlighted() {
|
169 |
this.showPopover();
|
170 |
|
|
|
|
|
171 |
this.highlightFinished = true;
|
172 |
|
173 |
const highlightedElement = this;
|
|
|
138 |
*/
|
139 |
onDeselected() {
|
140 |
this.hidePopover();
|
141 |
+
|
142 |
+
this.node.classList.remove('driver-highlighted-element');
|
143 |
+
|
144 |
this.highlightFinished = false;
|
145 |
|
146 |
if (this.options.onDeselected) {
|
|
|
148 |
}
|
149 |
}
|
150 |
|
151 |
+
getSize() {
|
152 |
+
const boundingRect = this.node.getBoundingClientRect();
|
153 |
+
return {
|
154 |
+
width: boundingRect.width,
|
155 |
+
height: boundingRect.height
|
156 |
+
};
|
157 |
+
}
|
158 |
+
|
159 |
/**
|
160 |
* Is called when the element is about to be highlighted
|
161 |
* i.e. either if overlay has started moving the highlight towards
|
|
|
179 |
onHighlighted() {
|
180 |
this.showPopover();
|
181 |
|
182 |
+
this.node.classList.add('driver-highlighted-element');
|
183 |
+
|
184 |
this.highlightFinished = true;
|
185 |
|
186 |
const highlightedElement = this;
|
src/core/overlay.js
CHANGED
@@ -1,5 +1,4 @@
|
|
1 |
import Position from './position';
|
2 |
-
import { ID_OVERLAY, OVERLAY_ZINDEX } from '../common/constants';
|
3 |
|
4 |
/**
|
5 |
* Responsible for overlay creation and manipulation i.e.
|
@@ -14,10 +13,8 @@ export default class Overlay {
|
|
14 |
constructor(options, window, document) {
|
15 |
this.options = options;
|
16 |
|
17 |
-
this.overlayAlpha = 0; // Is used to animate the layover
|
18 |
this.positionToHighlight = new Position({}); // position at which layover is to be patched at
|
19 |
this.highlightedPosition = new Position({}); // position at which layover is patched currently
|
20 |
-
this.redrawAnimation = null; // used to cancel the redraw animation
|
21 |
this.highlightedElement = null; // currently highlighted dom element (instance of Element)
|
22 |
this.lastHighlightedElement = null; // element that was highlighted before current one
|
23 |
|
@@ -27,31 +24,16 @@ export default class Overlay {
|
|
27 |
this.document = document;
|
28 |
|
29 |
this.resetOverlay();
|
30 |
-
this.setSize();
|
31 |
}
|
32 |
|
33 |
/**
|
34 |
* Prepares the overlay
|
35 |
*/
|
36 |
resetOverlay() {
|
37 |
-
//
|
38 |
-
const canvasOverlay = this.document.getElementById(ID_OVERLAY);
|
39 |
-
if (canvasOverlay && canvasOverlay.parentNode) {
|
40 |
-
canvasOverlay.parentNode.removeChild(canvasOverlay);
|
41 |
-
}
|
42 |
-
|
43 |
-
const overlay = this.document.createElement('canvas');
|
44 |
|
45 |
-
this.
|
46 |
-
this.
|
47 |
-
|
48 |
-
this.overlay.id = ID_OVERLAY;
|
49 |
-
this.overlay.style.pointerEvents = 'none';
|
50 |
-
this.overlay.style.background = 'transparent';
|
51 |
-
this.overlay.style.position = 'fixed';
|
52 |
-
this.overlay.style.top = '0';
|
53 |
-
this.overlay.style.left = '0';
|
54 |
-
this.overlay.style.zIndex = OVERLAY_ZINDEX;
|
55 |
}
|
56 |
|
57 |
/**
|
@@ -65,9 +47,6 @@ export default class Overlay {
|
|
65 |
return;
|
66 |
}
|
67 |
|
68 |
-
// @todo put it in the caller after testing
|
69 |
-
this.setSize();
|
70 |
-
|
71 |
// Trigger the hook for highlight started
|
72 |
element.onHighlightStarted();
|
73 |
|
@@ -123,6 +102,9 @@ export default class Overlay {
|
|
123 |
this.highlightedElement = null;
|
124 |
this.lastHighlightedElement = null;
|
125 |
|
|
|
|
|
|
|
126 |
this.draw();
|
127 |
}
|
128 |
|
@@ -132,135 +114,26 @@ export default class Overlay {
|
|
132 |
* Slowly eases towards the item to be selected.
|
133 |
*/
|
134 |
draw() {
|
135 |
-
|
136 |
-
|
137 |
-
|
138 |
-
// Remove the existing cloak from the body
|
139 |
-
// it might be torn i.e. have patches from last highlight
|
140 |
-
this.removeCloak();
|
141 |
-
// Add the overlay on top of the whole body
|
142 |
-
this.addCloak();
|
143 |
-
|
144 |
-
const isFadingIn = this.overlayAlpha < 0.1;
|
145 |
-
|
146 |
-
if (canHighlight) {
|
147 |
-
if (isFadingIn) {
|
148 |
-
// Ignore the animation, just highlight the item at its current position
|
149 |
-
this.highlightedPosition = this.positionToHighlight;
|
150 |
-
} else {
|
151 |
-
// Slowly move towards the position to highlight
|
152 |
-
this.highlightedPosition.left += (this.positionToHighlight.left - this.highlightedPosition.left) * 0.18;
|
153 |
-
this.highlightedPosition.top += (this.positionToHighlight.top - this.highlightedPosition.top) * 0.18;
|
154 |
-
this.highlightedPosition.right += (this.positionToHighlight.right - this.highlightedPosition.right) * 0.18;
|
155 |
-
this.highlightedPosition.bottom += (this.positionToHighlight.bottom - this.highlightedPosition.bottom) * 0.18;
|
156 |
-
}
|
157 |
-
}
|
158 |
-
|
159 |
-
// Cut the chunk of overlay that is over the highlighted item
|
160 |
-
this.removeCloak({
|
161 |
-
posX: this.highlightedPosition.left - this.window.scrollX - this.options.padding,
|
162 |
-
posY: this.highlightedPosition.top - this.window.scrollY - this.options.padding,
|
163 |
-
width: (this.highlightedPosition.right - this.highlightedPosition.left) + (this.options.padding * 2),
|
164 |
-
height: (this.highlightedPosition.bottom - this.highlightedPosition.top) + (this.options.padding * 2),
|
165 |
-
});
|
166 |
-
|
167 |
-
// Fade the overlay in if we can highlight
|
168 |
-
if (canHighlight) {
|
169 |
-
if (!this.options.animate) {
|
170 |
-
this.overlayAlpha = this.options.opacity;
|
171 |
-
} else {
|
172 |
-
this.overlayAlpha += (this.options.opacity - this.overlayAlpha) * 0.08;
|
173 |
-
}
|
174 |
-
} else {
|
175 |
-
// otherwise fade out
|
176 |
-
this.overlayAlpha = Math.max((this.overlayAlpha * 0.85) - 0.02, 0);
|
177 |
-
}
|
178 |
-
|
179 |
-
// cancel any existing animation frames
|
180 |
-
// to avoid the overlapping of frames
|
181 |
-
this.window.cancelAnimationFrame(this.redrawAnimation);
|
182 |
-
|
183 |
-
// Continue drawing while we can highlight or we are still fading out
|
184 |
-
if (canHighlight || this.overlayAlpha > 0) {
|
185 |
-
// Add the overlay if not already there
|
186 |
-
if (!this.overlay.parentNode) {
|
187 |
-
this.document.body.appendChild(this.overlay);
|
188 |
-
}
|
189 |
-
|
190 |
-
// Stage a new animation frame only if the position has not been reached
|
191 |
-
// or the alpha has not yet fully reached fully required opacity
|
192 |
-
if (!this.hasPositionHighlighted()) {
|
193 |
-
this.redrawAnimation = this.window.requestAnimationFrame(this.draw);
|
194 |
-
} else if (!this.options.animate && isFadingIn) {
|
195 |
-
this.redrawAnimation = this.window.requestAnimationFrame(this.draw);
|
196 |
-
} else {
|
197 |
-
// Element has been highlighted
|
198 |
-
this.highlightedElement.onHighlighted();
|
199 |
-
}
|
200 |
-
} else if (this.overlay.parentNode) {
|
201 |
-
// Otherwise if the overlay is there, remove it
|
202 |
-
this.document.body.removeChild(this.overlay);
|
203 |
}
|
204 |
-
}
|
205 |
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
*/
|
210 |
-
hasPositionHighlighted() {
|
211 |
-
return this.positionToHighlight.equals(this.highlightedPosition) &&
|
212 |
-
this.overlayAlpha > (this.options.opacity - 0.05);
|
213 |
-
}
|
214 |
|
215 |
-
|
216 |
-
* Removes the cloak from the given position
|
217 |
-
* i.e. cuts the chunk of layout which is over the element
|
218 |
-
* to be highlighted
|
219 |
-
*
|
220 |
-
* @param {number} posX
|
221 |
-
* @param {number} posY
|
222 |
-
* @param {number} width
|
223 |
-
* @param {number} height
|
224 |
-
*/
|
225 |
-
removeCloak({
|
226 |
-
posX = 0,
|
227 |
-
posY = 0,
|
228 |
-
width = this.overlay.width,
|
229 |
-
height = this.overlay.height,
|
230 |
-
} = {}) {
|
231 |
-
this.context.clearRect(posX, posY, width, height);
|
232 |
-
}
|
233 |
|
234 |
-
|
235 |
-
|
236 |
-
|
237 |
-
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
* @param {number} height
|
242 |
-
*/
|
243 |
-
addCloak({
|
244 |
-
posX = 0,
|
245 |
-
posY = 0,
|
246 |
-
width = this.overlay.width,
|
247 |
-
height = this.overlay.height,
|
248 |
-
} = {}) {
|
249 |
-
this.context.fillStyle = `rgba( 0, 0, 0, ${this.overlayAlpha} )`;
|
250 |
-
this.context.fillRect(posX, posY, width, height);
|
251 |
-
}
|
252 |
|
253 |
-
|
254 |
-
|
255 |
-
*
|
256 |
-
* @param {number|null} width
|
257 |
-
* @param {number|null} height
|
258 |
-
*/
|
259 |
-
setSize(width = null, height = null) {
|
260 |
-
// By default it is going to cover the whole page and then we will
|
261 |
-
// cut out a chunk for the element to be visible out of it
|
262 |
-
this.overlay.width = width || this.window.innerWidth;
|
263 |
-
this.overlay.height = height || this.window.innerHeight;
|
264 |
}
|
265 |
|
266 |
/**
|
@@ -270,13 +143,10 @@ export default class Overlay {
|
|
270 |
* @param {boolean} animate
|
271 |
*/
|
272 |
refresh(animate = true) {
|
273 |
-
this.setSize();
|
274 |
-
|
275 |
// If the highlighted element was there Cancel the
|
276 |
// existing animation frame if any and highlight it again
|
277 |
// as its position might have been changed
|
278 |
if (this.highlightedElement) {
|
279 |
-
this.window.cancelAnimationFrame(this.redrawAnimation);
|
280 |
this.highlight(this.highlightedElement, animate);
|
281 |
this.highlightedElement.onHighlighted();
|
282 |
}
|
|
|
1 |
import Position from './position';
|
|
|
2 |
|
3 |
/**
|
4 |
* Responsible for overlay creation and manipulation i.e.
|
|
|
13 |
constructor(options, window, document) {
|
14 |
this.options = options;
|
15 |
|
|
|
16 |
this.positionToHighlight = new Position({}); // position at which layover is to be patched at
|
17 |
this.highlightedPosition = new Position({}); // position at which layover is patched currently
|
|
|
18 |
this.highlightedElement = null; // currently highlighted dom element (instance of Element)
|
19 |
this.lastHighlightedElement = null; // element that was highlighted before current one
|
20 |
|
|
|
24 |
this.document = document;
|
25 |
|
26 |
this.resetOverlay();
|
|
|
27 |
}
|
28 |
|
29 |
/**
|
30 |
* Prepares the overlay
|
31 |
*/
|
32 |
resetOverlay() {
|
33 |
+
// @todo: append the elements if not there already
|
|
|
|
|
|
|
|
|
|
|
|
|
34 |
|
35 |
+
this.pageOverlay = this.document.getElementById('driver-page-overlay');
|
36 |
+
this.highlightStage = this.document.getElementById('driver-highlighted-element-stage');
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
37 |
}
|
38 |
|
39 |
/**
|
|
|
47 |
return;
|
48 |
}
|
49 |
|
|
|
|
|
|
|
50 |
// Trigger the hook for highlight started
|
51 |
element.onHighlightStarted();
|
52 |
|
|
|
102 |
this.highlightedElement = null;
|
103 |
this.lastHighlightedElement = null;
|
104 |
|
105 |
+
this.pageOverlay.style.display = 'none';
|
106 |
+
this.highlightStage.style.display = 'none';
|
107 |
+
|
108 |
this.draw();
|
109 |
}
|
110 |
|
|
|
114 |
* Slowly eases towards the item to be selected.
|
115 |
*/
|
116 |
draw() {
|
117 |
+
if (!this.highlightedElement || !this.positionToHighlight.canHighlight()) {
|
118 |
+
return;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
119 |
}
|
|
|
120 |
|
121 |
+
// Show the overlay
|
122 |
+
this.pageOverlay.style.display = 'block';
|
123 |
+
this.highlightStage.style.display = 'block';
|
|
|
|
|
|
|
|
|
|
|
124 |
|
125 |
+
const elementSize = this.highlightedElement.getSize();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
+
// Show the stage
|
128 |
+
this.highlightStage.style.display = 'block';
|
129 |
+
this.highlightStage.style.position = 'absolute';
|
130 |
+
this.highlightStage.style.width = `${elementSize.width + this.options.padding}px`;
|
131 |
+
this.highlightStage.style.height = `${elementSize.height + this.options.padding}px`;
|
132 |
+
this.highlightStage.style.top = `${this.positionToHighlight.top}px`;
|
133 |
+
this.highlightStage.style.left = `${this.positionToHighlight.left}px`;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
134 |
|
135 |
+
// Element has been highlighted
|
136 |
+
this.highlightedElement.onHighlighted();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
}
|
138 |
|
139 |
/**
|
|
|
143 |
* @param {boolean} animate
|
144 |
*/
|
145 |
refresh(animate = true) {
|
|
|
|
|
146 |
// If the highlighted element was there Cancel the
|
147 |
// existing animation frame if any and highlight it again
|
148 |
// as its position might have been changed
|
149 |
if (this.highlightedElement) {
|
|
|
150 |
this.highlight(this.highlightedElement, animate);
|
151 |
this.highlightedElement.onHighlighted();
|
152 |
}
|
src/driver.scss
CHANGED
@@ -105,4 +105,33 @@ div#driver-popover-item {
|
|
105 |
font-weight: normal;
|
106 |
zoom: 1;
|
107 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
108 |
}
|
|
|
105 |
font-weight: normal;
|
106 |
zoom: 1;
|
107 |
}
|
108 |
+
}
|
109 |
+
|
110 |
+
div#driver-page-overlay {
|
111 |
+
display: none;
|
112 |
+
width: 100%;
|
113 |
+
height: 100%;
|
114 |
+
background: black;
|
115 |
+
top: 0;
|
116 |
+
left: 0;
|
117 |
+
position: fixed;
|
118 |
+
opacity: 0.7;
|
119 |
+
pointer-events: none;
|
120 |
+
z-index: 100002 !important;
|
121 |
+
}
|
122 |
+
|
123 |
+
div#driver-highlighted-element-stage {
|
124 |
+
position: absolute;
|
125 |
+
top: 0;
|
126 |
+
left: 0;
|
127 |
+
height: 50px;
|
128 |
+
width: 300px;
|
129 |
+
background: white;
|
130 |
+
z-index: 100003 !important;
|
131 |
+
display: none;
|
132 |
+
}
|
133 |
+
|
134 |
+
.driver-highlighted-element {
|
135 |
+
z-index: 100004 !important;
|
136 |
+
position: relative;
|
137 |
}
|
src/index.js
CHANGED
@@ -45,7 +45,6 @@ export default class Driver {
|
|
45 |
this.steps = []; // steps to be presented if any
|
46 |
this.currentStep = 0; // index for the currently highlighted step
|
47 |
|
48 |
-
this.onScroll = this.onScroll.bind(this);
|
49 |
this.onResize = this.onResize.bind(this);
|
50 |
this.onKeyUp = this.onKeyUp.bind(this);
|
51 |
this.onClick = this.onClick.bind(this);
|
@@ -59,8 +58,6 @@ export default class Driver {
|
|
59 |
* @todo: add throttling in all the listeners
|
60 |
*/
|
61 |
bind() {
|
62 |
-
this.document.addEventListener('scroll', this.onScroll, false);
|
63 |
-
this.document.addEventListener('DOMMouseScroll', this.onScroll, false);
|
64 |
this.window.addEventListener('resize', this.onResize, false);
|
65 |
this.window.addEventListener('keyup', this.onKeyUp, false);
|
66 |
this.window.addEventListener('click', this.onClick, false);
|
@@ -179,19 +176,6 @@ export default class Driver {
|
|
179 |
return this.overlay.getLastHighlightedElement();
|
180 |
}
|
181 |
|
182 |
-
/**
|
183 |
-
* Handler for the onScroll event on document
|
184 |
-
* Refreshes without animation on scroll to make sure
|
185 |
-
* that the highlighted part travels with the scroll
|
186 |
-
*/
|
187 |
-
onScroll() {
|
188 |
-
if (!this.isActivated) {
|
189 |
-
return;
|
190 |
-
}
|
191 |
-
|
192 |
-
this.overlay.refresh(false);
|
193 |
-
}
|
194 |
-
|
195 |
/**
|
196 |
* Handler for the onResize DOM event
|
197 |
* Refreshes with animation on scroll to make sure that
|
|
|
45 |
this.steps = []; // steps to be presented if any
|
46 |
this.currentStep = 0; // index for the currently highlighted step
|
47 |
|
|
|
48 |
this.onResize = this.onResize.bind(this);
|
49 |
this.onKeyUp = this.onKeyUp.bind(this);
|
50 |
this.onClick = this.onClick.bind(this);
|
|
|
58 |
* @todo: add throttling in all the listeners
|
59 |
*/
|
60 |
bind() {
|
|
|
|
|
61 |
this.window.addEventListener('resize', this.onResize, false);
|
62 |
this.window.addEventListener('keyup', this.onKeyUp, false);
|
63 |
this.window.addEventListener('click', this.onClick, false);
|
|
|
176 |
return this.overlay.getLastHighlightedElement();
|
177 |
}
|
178 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
/**
|
180 |
* Handler for the onResize DOM event
|
181 |
* Refreshes with animation on scroll to make sure that
|
webpack.config.dev.js
CHANGED
@@ -18,16 +18,16 @@ module.exports = {
|
|
18 |
},
|
19 |
module: {
|
20 |
rules: [
|
21 |
-
{
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
|
30 |
-
},
|
31 |
{
|
32 |
test: /\.js$/,
|
33 |
exclude: /node_modules/,
|
|
|
18 |
},
|
19 |
module: {
|
20 |
rules: [
|
21 |
+
// {
|
22 |
+
// test: /\.js$/,
|
23 |
+
// exclude: /node_modules/,
|
24 |
+
// loader: 'eslint-loader',
|
25 |
+
// enforce: 'pre',
|
26 |
+
// options: {
|
27 |
+
// failOnWarning: false,
|
28 |
+
// failOnError: true,
|
29 |
+
// },
|
30 |
+
// },
|
31 |
{
|
32 |
test: /\.js$/,
|
33 |
exclude: /node_modules/,
|