Skip to content

Commit fed7f66

Browse files
committed
support clone canvas
1 parent f8c4e1c commit fed7f66

File tree

7 files changed

+97
-3
lines changed

7 files changed

+97
-3
lines changed

package.json

+1
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
"babel-plugin-transform-es2015-modules-commonjs": "^6.26.2",
5555
"babel-plugin-transform-object-rest-spread": "^6.26.0",
5656
"babel-preset-shopify": "^16.5.0",
57+
"canvas": "^1.6.13",
5758
"codecov": "^3.0.2",
5859
"concurrently": "^3.5.1",
5960
"core-js": "^2.5.7",

src/Draggable/Draggable.js

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {closest} from 'shared/utils';
1+
import {closest, cloneNode} from 'shared/utils';
22

33
import {Announcement, Focusable, Mirror, Scrollable} from './Plugins';
44

@@ -374,7 +374,7 @@ export default class Draggable {
374374
this.lastPlacedContainer.classList.remove(this.getClassNameFor('container:placed'));
375375
}
376376

377-
this.source = this.originalSource.cloneNode(true);
377+
this.source = cloneNode(this.originalSource, true);
378378
this.originalSource.parentNode.insertBefore(this.source, this.originalSource);
379379
this.originalSource.style.display = 'none';
380380

src/Draggable/Plugins/Mirror/Mirror.js

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import AbstractPlugin from 'shared/AbstractPlugin';
2+
import {cloneNode} from 'shared/utils';
23

34
import {
45
MirrorCreateEvent,
@@ -163,7 +164,7 @@ export default class Mirror extends AbstractPlugin {
163164
}
164165

165166
const appendableContainer = this[getAppendableContainer](source) || sourceContainer;
166-
this.mirror = source.cloneNode(true);
167+
this.mirror = cloneNode(source, true);
167168

168169
const mirrorCreatedEvent = new MirrorCreatedEvent({
169170
source,
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
/**
2+
* Get the new clone with canvas content cloned for a given node.
3+
*
4+
* @param {Node} node The node to clone
5+
* @param {Boolean} deep equal to the deep parameter of Node.cloneNode(), default false
6+
* @return {Node} new clone with canvas content cloned
7+
*/
8+
export default function cloneNode(node, deep = false) {
9+
const newClone = node.cloneNode(deep);
10+
let canvases;
11+
let newCanvases;
12+
if (node.tagName === 'CANVAS') {
13+
canvases = [node];
14+
newCanvases = [newClone];
15+
} else {
16+
canvases = node.querySelectorAll('canvas');
17+
newCanvases = newClone.querySelectorAll('canvas');
18+
}
19+
[...newCanvases].forEach((newCanvas, i) => {
20+
newCanvas.getContext('2d').drawImage(canvases[i], 0, 0);
21+
});
22+
return newClone;
23+
}

src/shared/utils/cloneNode/index.js

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import cloneNode from './cloneNode';
2+
3+
export default cloneNode;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import {createSandbox} from 'helper';
2+
import cloneNode from '../cloneNode';
3+
4+
const sampleMarkup = `
5+
<div class="CanvasContainer">
6+
<div class="FirstDiv">First</div>
7+
<canvas class="FirstCanvas" width="100" height="100"></canvas>
8+
<div class="SecondDiv">Second</div>
9+
<canvas class="SecondCanvas" widht="100" height="100"></canvas>
10+
</div>
11+
`;
12+
13+
describe('utils', () => {
14+
let sandbox;
15+
let canvasContainer;
16+
let firstCanvas;
17+
let secondCanvas;
18+
let firstDiv;
19+
let secondDiv;
20+
21+
beforeEach(() => {
22+
sandbox = createSandbox(sampleMarkup);
23+
canvasContainer = sandbox.querySelector('.CanvasContainer');
24+
firstCanvas = canvasContainer.querySelector('.FirstCanvas');
25+
secondCanvas = canvasContainer.querySelector('.SecondCanvas');
26+
firstDiv = canvasContainer.querySelector('.FirstDiv');
27+
secondDiv = canvasContainer.querySelector('.SecondDiv');
28+
29+
const firstCtx = firstCanvas.getContext('2d');
30+
const secondCtx = secondCanvas.getContext('2d');
31+
32+
firstCtx.fillRect(0, 0, 10, 10);
33+
secondCtx.fillRect(10, 10, 20, 20);
34+
});
35+
36+
afterEach(() => {
37+
sandbox.parentNode.removeChild(sandbox);
38+
});
39+
40+
it('clone canvas content when cloned node has canvas elements', () => {
41+
const newClone = cloneNode(canvasContainer, true);
42+
const newFirstCanvas = newClone.querySelector('.FirstCanvas');
43+
const newSecondCanvas = newClone.querySelector('.SecondCanvas');
44+
expect(newFirstCanvas.toDataURL()).toEqual(firstCanvas.toDataURL());
45+
expect(newSecondCanvas.toDataURL()).toEqual(secondCanvas.toDataURL());
46+
});
47+
48+
it('clone nodes when cloned node has canvas elements', () => {
49+
const newClone = cloneNode(canvasContainer, true);
50+
const newFirstDiv = newClone.querySelector('.FirstDiv');
51+
const newSecondDiv = newClone.querySelector('.SecondDiv');
52+
expect(newFirstDiv.innerHTML).toEqual(firstDiv.innerHTML);
53+
expect(newSecondDiv.innerHTML).toEqual(secondDiv.innerHTML);
54+
});
55+
56+
it('clone canvas content when cloned node is canvas', () => {
57+
const newCanvas = cloneNode(firstCanvas, true);
58+
expect(newCanvas.toDataURL()).toEqual(firstCanvas.toDataURL());
59+
});
60+
61+
it('not clone child nodes when deep is false', () => {
62+
const newClone = cloneNode(canvasContainer);
63+
expect(newClone.childNodes).toHaveLength(0);
64+
});
65+
});

src/shared/utils/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
export {default as closest} from './closest';
22
export {default as requestNextAnimationFrame} from './requestNextAnimationFrame';
33
export {default as distance} from './distance';
4+
export {default as cloneNode} from './cloneNode';

0 commit comments

Comments
 (0)