kamrify commited on
Commit
dc1e1c1
·
1 Parent(s): e19deb4

WIP - Remove canvas and use HTML/CSS

Browse files
Files changed (6) hide show
  1. index.html +3 -0
  2. src/core/element.js +13 -0
  3. src/core/overlay.js +21 -151
  4. src/driver.scss +29 -0
  5. src/index.js +0 -16
  6. 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
- // Check and remove the canvas if it already exists
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.overlay = overlay;
46
- this.context = overlay.getContext('2d');
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
- // Cache the response of this for re-use below
136
- const canHighlight = this.positionToHighlight.canHighlight();
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
- * Checks if there as any position highlighted
208
- * @returns {boolean}
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
- * Adds the overlay i.e. to cover the given
236
- * position with dark overlay
237
- *
238
- * @param {number} posX
239
- * @param {number} posY
240
- * @param {number} width
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
- * Sets the size for the overlay
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
- 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/,
 
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/,