mirror of
https://github.com/kamranahmedse/driver.js.git
synced 2026-11-01 21:22:09 +08:00
WIP - Remove canvas and use HTML/CSS
This commit is contained in:
@@ -320,6 +320,9 @@ activeElement.getNode(); // Gets the DOM Element behind this element
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="driver-page-overlay"></div>
|
||||
<div id="driver-highlighted-element-stage"></div>
|
||||
|
||||
<script async src="//platform.twitter.com/widgets.js" charset="utf-8"></script>
|
||||
<script async defer src="//buttons.github.io/buttons.js"></script>
|
||||
<script src="//twemoji.maxcdn.com/2/twemoji.min.js?2.5"></script>
|
||||
|
||||
@@ -138,6 +138,9 @@ export default class Element {
|
||||
*/
|
||||
onDeselected() {
|
||||
this.hidePopover();
|
||||
|
||||
this.node.classList.remove('driver-highlighted-element');
|
||||
|
||||
this.highlightFinished = false;
|
||||
|
||||
if (this.options.onDeselected) {
|
||||
@@ -145,6 +148,14 @@ export default class Element {
|
||||
}
|
||||
}
|
||||
|
||||
getSize() {
|
||||
const boundingRect = this.node.getBoundingClientRect();
|
||||
return {
|
||||
width: boundingRect.width,
|
||||
height: boundingRect.height
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Is called when the element is about to be highlighted
|
||||
* i.e. either if overlay has started moving the highlight towards
|
||||
@@ -168,6 +179,8 @@ export default class Element {
|
||||
onHighlighted() {
|
||||
this.showPopover();
|
||||
|
||||
this.node.classList.add('driver-highlighted-element');
|
||||
|
||||
this.highlightFinished = true;
|
||||
|
||||
const highlightedElement = this;
|
||||
|
||||
+21
-151
@@ -1,5 +1,4 @@
|
||||
import Position from './position';
|
||||
import { ID_OVERLAY, OVERLAY_ZINDEX } from '../common/constants';
|
||||
|
||||
/**
|
||||
* Responsible for overlay creation and manipulation i.e.
|
||||
@@ -14,10 +13,8 @@ export default class Overlay {
|
||||
constructor(options, window, document) {
|
||||
this.options = options;
|
||||
|
||||
this.overlayAlpha = 0; // Is used to animate the layover
|
||||
this.positionToHighlight = new Position({}); // position at which layover is to be patched at
|
||||
this.highlightedPosition = new Position({}); // position at which layover is patched currently
|
||||
this.redrawAnimation = null; // used to cancel the redraw animation
|
||||
this.highlightedElement = null; // currently highlighted dom element (instance of Element)
|
||||
this.lastHighlightedElement = null; // element that was highlighted before current one
|
||||
|
||||
@@ -27,31 +24,16 @@ export default class Overlay {
|
||||
this.document = document;
|
||||
|
||||
this.resetOverlay();
|
||||
this.setSize();
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the overlay
|
||||
*/
|
||||
resetOverlay() {
|
||||
// Check and remove the canvas if it already exists
|
||||
const canvasOverlay = this.document.getElementById(ID_OVERLAY);
|
||||
if (canvasOverlay && canvasOverlay.parentNode) {
|
||||
canvasOverlay.parentNode.removeChild(canvasOverlay);
|
||||
}
|
||||
// @todo: append the elements if not there already
|
||||
|
||||
const overlay = this.document.createElement('canvas');
|
||||
|
||||
this.overlay = overlay;
|
||||
this.context = overlay.getContext('2d');
|
||||
|
||||
this.overlay.id = ID_OVERLAY;
|
||||
this.overlay.style.pointerEvents = 'none';
|
||||
this.overlay.style.background = 'transparent';
|
||||
this.overlay.style.position = 'fixed';
|
||||
this.overlay.style.top = '0';
|
||||
this.overlay.style.left = '0';
|
||||
this.overlay.style.zIndex = OVERLAY_ZINDEX;
|
||||
this.pageOverlay = this.document.getElementById('driver-page-overlay');
|
||||
this.highlightStage = this.document.getElementById('driver-highlighted-element-stage');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,9 +47,6 @@ export default class Overlay {
|
||||
return;
|
||||
}
|
||||
|
||||
// @todo put it in the caller after testing
|
||||
this.setSize();
|
||||
|
||||
// Trigger the hook for highlight started
|
||||
element.onHighlightStarted();
|
||||
|
||||
@@ -123,6 +102,9 @@ export default class Overlay {
|
||||
this.highlightedElement = null;
|
||||
this.lastHighlightedElement = null;
|
||||
|
||||
this.pageOverlay.style.display = 'none';
|
||||
this.highlightStage.style.display = 'none';
|
||||
|
||||
this.draw();
|
||||
}
|
||||
|
||||
@@ -132,135 +114,26 @@ export default class Overlay {
|
||||
* Slowly eases towards the item to be selected.
|
||||
*/
|
||||
draw() {
|
||||
// Cache the response of this for re-use below
|
||||
const canHighlight = this.positionToHighlight.canHighlight();
|
||||
|
||||
// Remove the existing cloak from the body
|
||||
// it might be torn i.e. have patches from last highlight
|
||||
this.removeCloak();
|
||||
// Add the overlay on top of the whole body
|
||||
this.addCloak();
|
||||
|
||||
const isFadingIn = this.overlayAlpha < 0.1;
|
||||
|
||||
if (canHighlight) {
|
||||
if (isFadingIn) {
|
||||
// Ignore the animation, just highlight the item at its current position
|
||||
this.highlightedPosition = this.positionToHighlight;
|
||||
} else {
|
||||
// Slowly move towards the position to highlight
|
||||
this.highlightedPosition.left += (this.positionToHighlight.left - this.highlightedPosition.left) * 0.18;
|
||||
this.highlightedPosition.top += (this.positionToHighlight.top - this.highlightedPosition.top) * 0.18;
|
||||
this.highlightedPosition.right += (this.positionToHighlight.right - this.highlightedPosition.right) * 0.18;
|
||||
this.highlightedPosition.bottom += (this.positionToHighlight.bottom - this.highlightedPosition.bottom) * 0.18;
|
||||
}
|
||||
if (!this.highlightedElement || !this.positionToHighlight.canHighlight()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cut the chunk of overlay that is over the highlighted item
|
||||
this.removeCloak({
|
||||
posX: this.highlightedPosition.left - this.window.scrollX - this.options.padding,
|
||||
posY: this.highlightedPosition.top - this.window.scrollY - this.options.padding,
|
||||
width: (this.highlightedPosition.right - this.highlightedPosition.left) + (this.options.padding * 2),
|
||||
height: (this.highlightedPosition.bottom - this.highlightedPosition.top) + (this.options.padding * 2),
|
||||
});
|
||||
// Show the overlay
|
||||
this.pageOverlay.style.display = 'block';
|
||||
this.highlightStage.style.display = 'block';
|
||||
|
||||
// Fade the overlay in if we can highlight
|
||||
if (canHighlight) {
|
||||
if (!this.options.animate) {
|
||||
this.overlayAlpha = this.options.opacity;
|
||||
} else {
|
||||
this.overlayAlpha += (this.options.opacity - this.overlayAlpha) * 0.08;
|
||||
}
|
||||
} else {
|
||||
// otherwise fade out
|
||||
this.overlayAlpha = Math.max((this.overlayAlpha * 0.85) - 0.02, 0);
|
||||
}
|
||||
const elementSize = this.highlightedElement.getSize();
|
||||
|
||||
// cancel any existing animation frames
|
||||
// to avoid the overlapping of frames
|
||||
this.window.cancelAnimationFrame(this.redrawAnimation);
|
||||
// Show the stage
|
||||
this.highlightStage.style.display = 'block';
|
||||
this.highlightStage.style.position = 'absolute';
|
||||
this.highlightStage.style.width = `${elementSize.width + this.options.padding}px`;
|
||||
this.highlightStage.style.height = `${elementSize.height + this.options.padding}px`;
|
||||
this.highlightStage.style.top = `${this.positionToHighlight.top}px`;
|
||||
this.highlightStage.style.left = `${this.positionToHighlight.left}px`;
|
||||
|
||||
// Continue drawing while we can highlight or we are still fading out
|
||||
if (canHighlight || this.overlayAlpha > 0) {
|
||||
// Add the overlay if not already there
|
||||
if (!this.overlay.parentNode) {
|
||||
this.document.body.appendChild(this.overlay);
|
||||
}
|
||||
|
||||
// Stage a new animation frame only if the position has not been reached
|
||||
// or the alpha has not yet fully reached fully required opacity
|
||||
if (!this.hasPositionHighlighted()) {
|
||||
this.redrawAnimation = this.window.requestAnimationFrame(this.draw);
|
||||
} else if (!this.options.animate && isFadingIn) {
|
||||
this.redrawAnimation = this.window.requestAnimationFrame(this.draw);
|
||||
} else {
|
||||
// Element has been highlighted
|
||||
this.highlightedElement.onHighlighted();
|
||||
}
|
||||
} else if (this.overlay.parentNode) {
|
||||
// Otherwise if the overlay is there, remove it
|
||||
this.document.body.removeChild(this.overlay);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if there as any position highlighted
|
||||
* @returns {boolean}
|
||||
*/
|
||||
hasPositionHighlighted() {
|
||||
return this.positionToHighlight.equals(this.highlightedPosition) &&
|
||||
this.overlayAlpha > (this.options.opacity - 0.05);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the cloak from the given position
|
||||
* i.e. cuts the chunk of layout which is over the element
|
||||
* to be highlighted
|
||||
*
|
||||
* @param {number} posX
|
||||
* @param {number} posY
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
*/
|
||||
removeCloak({
|
||||
posX = 0,
|
||||
posY = 0,
|
||||
width = this.overlay.width,
|
||||
height = this.overlay.height,
|
||||
} = {}) {
|
||||
this.context.clearRect(posX, posY, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the overlay i.e. to cover the given
|
||||
* position with dark overlay
|
||||
*
|
||||
* @param {number} posX
|
||||
* @param {number} posY
|
||||
* @param {number} width
|
||||
* @param {number} height
|
||||
*/
|
||||
addCloak({
|
||||
posX = 0,
|
||||
posY = 0,
|
||||
width = this.overlay.width,
|
||||
height = this.overlay.height,
|
||||
} = {}) {
|
||||
this.context.fillStyle = `rgba( 0, 0, 0, ${this.overlayAlpha} )`;
|
||||
this.context.fillRect(posX, posY, width, height);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the size for the overlay
|
||||
*
|
||||
* @param {number|null} width
|
||||
* @param {number|null} height
|
||||
*/
|
||||
setSize(width = null, height = null) {
|
||||
// By default it is going to cover the whole page and then we will
|
||||
// cut out a chunk for the element to be visible out of it
|
||||
this.overlay.width = width || this.window.innerWidth;
|
||||
this.overlay.height = height || this.window.innerHeight;
|
||||
// Element has been highlighted
|
||||
this.highlightedElement.onHighlighted();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -270,13 +143,10 @@ export default class Overlay {
|
||||
* @param {boolean} animate
|
||||
*/
|
||||
refresh(animate = true) {
|
||||
this.setSize();
|
||||
|
||||
// If the highlighted element was there Cancel the
|
||||
// existing animation frame if any and highlight it again
|
||||
// as its position might have been changed
|
||||
if (this.highlightedElement) {
|
||||
this.window.cancelAnimationFrame(this.redrawAnimation);
|
||||
this.highlight(this.highlightedElement, animate);
|
||||
this.highlightedElement.onHighlighted();
|
||||
}
|
||||
|
||||
@@ -105,4 +105,33 @@ div#driver-popover-item {
|
||||
font-weight: normal;
|
||||
zoom: 1;
|
||||
}
|
||||
}
|
||||
|
||||
div#driver-page-overlay {
|
||||
display: none;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: black;
|
||||
top: 0;
|
||||
left: 0;
|
||||
position: fixed;
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
z-index: 100002 !important;
|
||||
}
|
||||
|
||||
div#driver-highlighted-element-stage {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 50px;
|
||||
width: 300px;
|
||||
background: white;
|
||||
z-index: 100003 !important;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.driver-highlighted-element {
|
||||
z-index: 100004 !important;
|
||||
position: relative;
|
||||
}
|
||||
@@ -45,7 +45,6 @@ export default class Driver {
|
||||
this.steps = []; // steps to be presented if any
|
||||
this.currentStep = 0; // index for the currently highlighted step
|
||||
|
||||
this.onScroll = this.onScroll.bind(this);
|
||||
this.onResize = this.onResize.bind(this);
|
||||
this.onKeyUp = this.onKeyUp.bind(this);
|
||||
this.onClick = this.onClick.bind(this);
|
||||
@@ -59,8 +58,6 @@ export default class Driver {
|
||||
* @todo: add throttling in all the listeners
|
||||
*/
|
||||
bind() {
|
||||
this.document.addEventListener('scroll', this.onScroll, false);
|
||||
this.document.addEventListener('DOMMouseScroll', this.onScroll, false);
|
||||
this.window.addEventListener('resize', this.onResize, false);
|
||||
this.window.addEventListener('keyup', this.onKeyUp, false);
|
||||
this.window.addEventListener('click', this.onClick, false);
|
||||
@@ -179,19 +176,6 @@ export default class Driver {
|
||||
return this.overlay.getLastHighlightedElement();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the onScroll event on document
|
||||
* Refreshes without animation on scroll to make sure
|
||||
* that the highlighted part travels with the scroll
|
||||
*/
|
||||
onScroll() {
|
||||
if (!this.isActivated) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.overlay.refresh(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for the onResize DOM event
|
||||
* Refreshes with animation on scroll to make sure that
|
||||
|
||||
+10
-10
@@ -18,16 +18,16 @@ module.exports = {
|
||||
},
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
loader: 'eslint-loader',
|
||||
enforce: 'pre',
|
||||
options: {
|
||||
failOnWarning: false,
|
||||
failOnError: true,
|
||||
},
|
||||
},
|
||||
// {
|
||||
// test: /\.js$/,
|
||||
// exclude: /node_modules/,
|
||||
// loader: 'eslint-loader',
|
||||
// enforce: 'pre',
|
||||
// options: {
|
||||
// failOnWarning: false,
|
||||
// failOnError: true,
|
||||
// },
|
||||
// },
|
||||
{
|
||||
test: /\.js$/,
|
||||
exclude: /node_modules/,
|
||||
|
||||
Reference in New Issue
Block a user