Restructure and es6-ify
Browse files- assets/scripts/src/element.js +47 -0
- assets/scripts/src/overlay.js +74 -0
- assets/scripts/src/position.js +27 -0
- assets/scripts/src/sholo.js +19 -86
- index.html +14 -0
- package.json +4 -4
- webpack.config.dev.js +2 -1
- yarn.lock +4 -0
assets/scripts/src/element.js
ADDED
@@ -0,0 +1,47 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import Position from './position';
|
2 |
+
|
3 |
+
export default class Element {
|
4 |
+
constructor(node) {
|
5 |
+
this.element = node;
|
6 |
+
this.document = document;
|
7 |
+
}
|
8 |
+
|
9 |
+
// Gets the screen co-ordinates for the current dom element
|
10 |
+
getScreenCoordinates() {
|
11 |
+
let tempNode = this.element;
|
12 |
+
|
13 |
+
let x = this.document.documentElement.offsetLeft;
|
14 |
+
let y = this.document.documentElement.offsetTop;
|
15 |
+
|
16 |
+
if (tempNode.offsetParent) {
|
17 |
+
do {
|
18 |
+
x += tempNode.offsetLeft;
|
19 |
+
y += tempNode.offsetTop;
|
20 |
+
} while (tempNode = tempNode.offsetParent);
|
21 |
+
}
|
22 |
+
|
23 |
+
return { x, y };
|
24 |
+
}
|
25 |
+
|
26 |
+
// Gets the calculated position on screen
|
27 |
+
getPosition() {
|
28 |
+
const coordinates = this.getScreenCoordinates();
|
29 |
+
const position = new Position({
|
30 |
+
left: Number.MAX_VALUE,
|
31 |
+
top: Number.MAX_VALUE,
|
32 |
+
right: 0,
|
33 |
+
bottom: 0,
|
34 |
+
});
|
35 |
+
|
36 |
+
// If we have the position for this element
|
37 |
+
// and the element is visible on screen (has some height)
|
38 |
+
if (typeof coordinates.x === 'number' && typeof coordinates.y === 'number' && (this.element.offsetWidth > 0 || this.element.offsetHeight > 0)) {
|
39 |
+
position.left = Math.min(position.left, coordinates.x);
|
40 |
+
position.top = Math.min(position.top, coordinates.y);
|
41 |
+
position.right = Math.max(position.right, coordinates.x + this.element.offsetWidth);
|
42 |
+
position.bottom = Math.max(position.bottom, coordinates.y + this.element.offsetHeight);
|
43 |
+
}
|
44 |
+
|
45 |
+
return position;
|
46 |
+
}
|
47 |
+
}
|
assets/scripts/src/overlay.js
ADDED
@@ -0,0 +1,74 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import Position from './position';
|
2 |
+
|
3 |
+
/**
|
4 |
+
* Responsible for overlay creation and manipulation i.e.
|
5 |
+
* cutting out the visible part, animating between the sections etc
|
6 |
+
*/
|
7 |
+
export default class Overlay {
|
8 |
+
constructor({ alpha = 0.75 }) {
|
9 |
+
this.alpha = alpha;
|
10 |
+
this.window = window;
|
11 |
+
this.selected = new Position({});
|
12 |
+
|
13 |
+
this.prepareContext();
|
14 |
+
this.setSize();
|
15 |
+
}
|
16 |
+
|
17 |
+
// Prepares the overlay
|
18 |
+
prepareContext() {
|
19 |
+
const overlay = document.createElement('canvas');
|
20 |
+
|
21 |
+
this.overlay = overlay;
|
22 |
+
this.context = overlay.getContext('2d');
|
23 |
+
|
24 |
+
this.overlay.style.pointerEvents = 'none';
|
25 |
+
this.overlay.style.background = 'transparent';
|
26 |
+
this.overlay.style.position = 'fixed';
|
27 |
+
this.overlay.style.top = '0';
|
28 |
+
this.overlay.style.left = '0';
|
29 |
+
this.overlay.style.zIndex = '999999999';
|
30 |
+
}
|
31 |
+
|
32 |
+
// Highlights the dom element on the screen
|
33 |
+
highglight(element) {
|
34 |
+
if (!element) {
|
35 |
+
// @todo - clearing the overlay
|
36 |
+
return;
|
37 |
+
}
|
38 |
+
|
39 |
+
// get the position of element around which we need to draw
|
40 |
+
const position = element.getPosition();
|
41 |
+
if (!position.isValid()) {
|
42 |
+
return;
|
43 |
+
}
|
44 |
+
|
45 |
+
this.selected = position;
|
46 |
+
this.draw();
|
47 |
+
}
|
48 |
+
|
49 |
+
draw() {
|
50 |
+
this.context.clearRect(0, 0, this.overlay.width, this.overlay.height);
|
51 |
+
this.context.fillStyle = `rgba( 0, 0, 0, ${this.alpha})`;
|
52 |
+
this.context.fillRect(0, 0, this.overlay.width, this.overlay.height);
|
53 |
+
|
54 |
+
// Cut out the cleared region
|
55 |
+
this.context.clearRect(
|
56 |
+
this.selected.left - window.scrollX,
|
57 |
+
this.selected.top - window.scrollY,
|
58 |
+
(this.selected.right - this.selected.left),
|
59 |
+
(this.selected.bottom - this.selected.top),
|
60 |
+
);
|
61 |
+
|
62 |
+
// Append the overlay if not there already
|
63 |
+
if (!this.overlay.parentNode) {
|
64 |
+
document.body.appendChild(this.overlay);
|
65 |
+
}
|
66 |
+
}
|
67 |
+
|
68 |
+
setSize(width = null, height = null) {
|
69 |
+
// By default it is going to cover the whole page and then we will
|
70 |
+
// cut out a chunk for the element to be visible out of it
|
71 |
+
this.overlay.width = width || this.window.innerWidth;
|
72 |
+
this.overlay.height = height || this.window.innerHeight;
|
73 |
+
}
|
74 |
+
}
|
assets/scripts/src/position.js
ADDED
@@ -0,0 +1,27 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
/**
|
2 |
+
* Responsible for validating positions and is used
|
3 |
+
* when manipulating positions across the application
|
4 |
+
*/
|
5 |
+
export default class Position {
|
6 |
+
/**
|
7 |
+
* @param left
|
8 |
+
* @param top
|
9 |
+
* @param right
|
10 |
+
* @param bottom
|
11 |
+
*/
|
12 |
+
constructor({
|
13 |
+
left = 0,
|
14 |
+
top = 0,
|
15 |
+
right = 0,
|
16 |
+
bottom = 0,
|
17 |
+
}) {
|
18 |
+
this.left = left;
|
19 |
+
this.right = right;
|
20 |
+
this.top = top;
|
21 |
+
this.bottom = bottom;
|
22 |
+
}
|
23 |
+
|
24 |
+
isValid() {
|
25 |
+
return this.left < this.right && this.top < this.bottom;
|
26 |
+
}
|
27 |
+
}
|
assets/scripts/src/sholo.js
CHANGED
@@ -1,94 +1,27 @@
|
|
1 |
-
|
2 |
-
|
3 |
|
4 |
-
// overlay is going to cover the whole page and then we will
|
5 |
-
// cut out a chunk for the element to be visible out of it
|
6 |
-
function createOverlay() {
|
7 |
-
overlay = document.createElement('canvas');
|
8 |
-
overlayContext = overlay.getContext('2d');
|
9 |
|
10 |
-
|
11 |
-
|
12 |
-
|
13 |
-
|
14 |
-
|
15 |
-
|
16 |
-
overlay.width = window.innerWidth;
|
17 |
-
overlay.height = window.innerHeight;
|
18 |
-
|
19 |
-
overlayContext.clearRect(0, 0, overlay.width, overlay.height);
|
20 |
-
overlayContext.fillStyle = 'rgba( 0, 0, 0, 0.7)';
|
21 |
-
overlayContext.fillRect(0, 0, overlay.width, overlay.height);
|
22 |
-
}
|
23 |
-
|
24 |
-
// Finds the correct position of node on screen
|
25 |
-
function getNodePosition(node) {
|
26 |
-
let x = document.documentElement.offsetLeft;
|
27 |
-
let y = document.documentElement.offsetTop;
|
28 |
-
|
29 |
-
if (node.offsetParent) {
|
30 |
-
do {
|
31 |
-
x += node.offsetLeft;
|
32 |
-
y += node.offsetTop;
|
33 |
-
} while (node = node.offsetParent);
|
34 |
}
|
35 |
|
36 |
-
|
37 |
-
|
38 |
|
39 |
-
|
40 |
-
|
41 |
-
|
42 |
-
|
43 |
-
|
44 |
-
|
45 |
-
|
46 |
-
const currentRegion = {
|
47 |
-
left: Number.MAX_VALUE, top: Number.MAX_VALUE, right: 0, bottom: 0,
|
48 |
-
};
|
49 |
-
const nodePosition = getNodePosition(node);
|
50 |
|
51 |
-
|
52 |
-
|
53 |
-
currentRegion.left = Math.min(currentRegion.left, nodePosition.x);
|
54 |
-
currentRegion.top = Math.min(currentRegion.top, nodePosition.y);
|
55 |
-
currentRegion.right = Math.max(currentRegion.right, nodePosition.x + node.offsetWidth);
|
56 |
-
currentRegion.bottom = Math.max(currentRegion.bottom, nodePosition.y + node.offsetHeight);
|
57 |
}
|
58 |
-
|
59 |
-
|
60 |
-
const isValidRegion = currentRegion.left < currentRegion.right && currentRegion.top < currentRegion.bottom;
|
61 |
-
if (!isValidRegion) {
|
62 |
-
return;
|
63 |
-
}
|
64 |
-
|
65 |
-
const overlayAlpha = 0.7;
|
66 |
-
|
67 |
-
// Reset the overlay
|
68 |
-
overlayContext.clearRect(0, 0, overlay.width, overlay.height);
|
69 |
-
overlayContext.fillStyle = `rgba( 0, 0, 0, ${overlayAlpha} )`;
|
70 |
-
overlayContext.fillRect(0, 0, overlay.width, overlay.height);
|
71 |
-
|
72 |
-
// Cut out the cleared region
|
73 |
-
overlayContext.clearRect(
|
74 |
-
currentRegion.left - window.scrollX,
|
75 |
-
currentRegion.top - window.scrollY,
|
76 |
-
(currentRegion.right - currentRegion.left),
|
77 |
-
(currentRegion.bottom - currentRegion.top),
|
78 |
-
);
|
79 |
-
|
80 |
-
document.body.appendChild(overlay);
|
81 |
}
|
82 |
-
|
83 |
-
const nodesToSelect = [
|
84 |
-
document.querySelector('.section__header'),
|
85 |
-
document.querySelector('.section__how'),
|
86 |
-
];
|
87 |
-
|
88 |
-
createOverlay();
|
89 |
-
|
90 |
-
nodesToSelect.forEach((nodeToSelect, index) => {
|
91 |
-
window.setTimeout(() => {
|
92 |
-
selectNode(nodeToSelect);
|
93 |
-
}, index * 1000);
|
94 |
-
});
|
|
|
1 |
+
import Overlay from './overlay';
|
2 |
+
import Element from './element';
|
3 |
|
|
|
|
|
|
|
|
|
|
|
4 |
|
5 |
+
/**
|
6 |
+
* Plugin class that drives the plugin
|
7 |
+
*/
|
8 |
+
export default class Sholo {
|
9 |
+
constructor({ alpha = 0.75 } = {}) {
|
10 |
+
this.overlay = new Overlay({ alpha });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
}
|
12 |
|
13 |
+
highlight(selector) {
|
14 |
+
let domElement;
|
15 |
|
16 |
+
if (typeof selector === 'string') {
|
17 |
+
domElement = document.querySelector(selector);
|
18 |
+
} else if (typeof selector === 'object') {
|
19 |
+
domElement = selector;
|
20 |
+
} else {
|
21 |
+
throw new Error('Element can only be string or the dom element');
|
22 |
+
}
|
|
|
|
|
|
|
|
|
23 |
|
24 |
+
const element = new Element(domElement);
|
25 |
+
this.overlay.highglight(element);
|
|
|
|
|
|
|
|
|
26 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
27 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
index.html
CHANGED
@@ -43,5 +43,19 @@
|
|
43 |
</div>
|
44 |
|
45 |
<script src="./assets/scripts/dist/sholo.js"></script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
46 |
</body>
|
47 |
</html>
|
|
|
43 |
</div>
|
44 |
|
45 |
<script src="./assets/scripts/dist/sholo.js"></script>
|
46 |
+
<script>
|
47 |
+
const nodesToSelect = [
|
48 |
+
'.section__header',
|
49 |
+
'.section__how',
|
50 |
+
];
|
51 |
+
|
52 |
+
const sholo = new Sholo();
|
53 |
+
|
54 |
+
nodesToSelect.forEach((nodeToSelect, index) => {
|
55 |
+
window.setTimeout(() => {
|
56 |
+
sholo.highlight(nodeToSelect);
|
57 |
+
}, index * 1000);
|
58 |
+
});
|
59 |
+
</script>
|
60 |
</body>
|
61 |
</html>
|
package.json
CHANGED
@@ -13,6 +13,7 @@
|
|
13 |
"babel-core": "^6.26.0",
|
14 |
"babel-eslint": "^8.2.2",
|
15 |
"babel-loader": "^7.1.3",
|
|
|
16 |
"babel-preset-env": "^1.6.1",
|
17 |
"css-loader": "^0.28.10",
|
18 |
"eslint": "^4.18.2",
|
@@ -21,6 +22,7 @@
|
|
21 |
"eslint-plugin-import": "^2.9.0",
|
22 |
"eslint-plugin-node": "^6.0.1",
|
23 |
"extract-loader": "^1.0.2",
|
|
|
24 |
"file-loader": "^1.1.11",
|
25 |
"node-sass": "^4.7.2",
|
26 |
"opn": "^5.2.0",
|
@@ -29,9 +31,7 @@
|
|
29 |
"style-loader": "^0.20.2",
|
30 |
"webpack": "^4.0.1",
|
31 |
"webpack-cli": "^2.0.10",
|
32 |
-
"webpack-dev-server": "^3.1.0"
|
33 |
-
"extract-text-webpack-plugin": "next"
|
34 |
},
|
35 |
-
"dependencies": {
|
36 |
-
}
|
37 |
}
|
|
|
13 |
"babel-core": "^6.26.0",
|
14 |
"babel-eslint": "^8.2.2",
|
15 |
"babel-loader": "^7.1.3",
|
16 |
+
"babel-plugin-add-module-exports": "^0.2.1",
|
17 |
"babel-preset-env": "^1.6.1",
|
18 |
"css-loader": "^0.28.10",
|
19 |
"eslint": "^4.18.2",
|
|
|
22 |
"eslint-plugin-import": "^2.9.0",
|
23 |
"eslint-plugin-node": "^6.0.1",
|
24 |
"extract-loader": "^1.0.2",
|
25 |
+
"extract-text-webpack-plugin": "next",
|
26 |
"file-loader": "^1.1.11",
|
27 |
"node-sass": "^4.7.2",
|
28 |
"opn": "^5.2.0",
|
|
|
31 |
"style-loader": "^0.20.2",
|
32 |
"webpack": "^4.0.1",
|
33 |
"webpack-cli": "^2.0.10",
|
34 |
+
"webpack-dev-server": "^3.1.0"
|
|
|
35 |
},
|
36 |
+
"dependencies": {}
|
|
|
37 |
}
|
webpack.config.dev.js
CHANGED
@@ -5,8 +5,8 @@ module.exports = {
|
|
5 |
mode: 'development',
|
6 |
entry: [
|
7 |
'webpack-dev-server/client?http://localhost:3000',
|
8 |
-
'./assets/scripts/src/sholo.js',
|
9 |
'./assets/styles/scss/demo.scss',
|
|
|
10 |
],
|
11 |
output: {
|
12 |
path: path.join(__dirname, '/assets'),
|
@@ -33,6 +33,7 @@ module.exports = {
|
|
33 |
loader: 'babel-loader',
|
34 |
options: {
|
35 |
presets: ['env'],
|
|
|
36 |
},
|
37 |
},
|
38 |
{
|
|
|
5 |
mode: 'development',
|
6 |
entry: [
|
7 |
'webpack-dev-server/client?http://localhost:3000',
|
|
|
8 |
'./assets/styles/scss/demo.scss',
|
9 |
+
'./assets/scripts/src/sholo.js',
|
10 |
],
|
11 |
output: {
|
12 |
path: path.join(__dirname, '/assets'),
|
|
|
33 |
loader: 'babel-loader',
|
34 |
options: {
|
35 |
presets: ['env'],
|
36 |
+
plugins: ['babel-plugin-add-module-exports'],
|
37 |
},
|
38 |
},
|
39 |
{
|
yarn.lock
CHANGED
@@ -546,6 +546,10 @@ babel-messages@^6.23.0:
|
|
546 |
dependencies:
|
547 |
babel-runtime "^6.22.0"
|
548 |
|
|
|
|
|
|
|
|
|
549 |
babel-plugin-check-es2015-constants@^6.22.0:
|
550 |
version "6.22.0"
|
551 |
resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
|
|
|
546 |
dependencies:
|
547 |
babel-runtime "^6.22.0"
|
548 |
|
549 |
+
babel-plugin-add-module-exports@^0.2.1:
|
550 |
+
version "0.2.1"
|
551 |
+
resolved "https://registry.yarnpkg.com/babel-plugin-add-module-exports/-/babel-plugin-add-module-exports-0.2.1.tgz#9ae9a1f4a8dc67f0cdec4f4aeda1e43a5ff65e25"
|
552 |
+
|
553 |
babel-plugin-check-es2015-constants@^6.22.0:
|
554 |
version "6.22.0"
|
555 |
resolved "https://registry.yarnpkg.com/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz#35157b101426fd2ffd3da3f75c7d1e91835bbf8a"
|