-
Notifications
You must be signed in to change notification settings - Fork 130
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add a CanvasLayer class to handle onscreen/offscreen canvases
- Loading branch information
Showing
2 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
'use babel' | ||
|
||
export default class CanvasLayer { | ||
constructor () { | ||
/** | ||
* The onscreen canvas. | ||
* @type {HTMLCanvasElement} | ||
*/ | ||
this.canvas = document.createElement('canvas') | ||
/** | ||
* The onscreen canvas context. | ||
* @type {CanvasRenderingContext2D} | ||
*/ | ||
this.context = this.canvas.getContext('2d') | ||
this.canvas.webkitImageSmoothingEnabled = false | ||
this.context.imageSmoothingEnabled = false | ||
|
||
/** | ||
* The offscreen canvas. | ||
* @type {HTMLCanvasElement} | ||
* @access private | ||
*/ | ||
this.offscreenCanvas = document.createElement('canvas') | ||
/** | ||
* The offscreen canvas context. | ||
* @type {CanvasRenderingContext2D} | ||
* @access private | ||
*/ | ||
this.offscreenContext = this.offscreenCanvas.getContext('2d') | ||
this.offscreenCanvas.webkitImageSmoothingEnabled = false | ||
this.offscreenContext.imageSmoothingEnabled = false | ||
} | ||
|
||
attach (parent) { | ||
if (this.canvas.parentNode) { return } | ||
|
||
parent.appendChild(this.canvas) | ||
} | ||
|
||
setSize (width = 0, height = 0) { | ||
this.canvas.width = width | ||
this.canvas.height = height | ||
this.context.imageSmoothingEnabled = false | ||
this.resetOffscreenSize() | ||
} | ||
|
||
resetOffscreenSize () { | ||
this.offscreenCanvas.width = this.canvas.width | ||
this.offscreenCanvas.height = this.canvas.height | ||
this.offscreenContext.imageSmoothingEnabled = false | ||
} | ||
|
||
copyToOffscreen () { | ||
this.offscreenContext.drawImage(this.canvas, 0, 0) | ||
} | ||
|
||
copyFromOffscreen () { | ||
this.context.drawImage(this.offscreenCanvas, 0, 0) | ||
} | ||
|
||
copyPartFromOffscreen (srcY, destY, height) { | ||
this.context.drawImage( | ||
this.offscreenCanvas, | ||
0, srcY, this.offscreenCanvas.width, height, | ||
0, destY, this.offscreenCanvas.width, height | ||
) | ||
} | ||
|
||
clearCanvas () { | ||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
'use babel' | ||
|
||
import CanvasLayer from '../lib/canvas-layer' | ||
|
||
describe('CanvasLayer', () => { | ||
let [layer] = [] | ||
|
||
beforeEach(() => { | ||
layer = new CanvasLayer() | ||
|
||
layer.setSize(100, 300) | ||
}) | ||
|
||
it('has two canvas', () => { | ||
expect(layer.canvas).toBeDefined() | ||
expect(layer.offscreenCanvas).toBeDefined() | ||
}) | ||
|
||
it('has a context for each canvas', () => { | ||
expect(layer.context).toBeDefined() | ||
expect(layer.offscreenContext).toBeDefined() | ||
}) | ||
|
||
it('disables the smoothing for the canvas', () => { | ||
expect(layer.canvas.webkitImageSmoothingEnabled).toBeFalsy() | ||
expect(layer.offscreenCanvas.webkitImageSmoothingEnabled).toBeFalsy() | ||
|
||
expect(layer.context.imageSmoothingEnabled).toBeFalsy() | ||
expect(layer.offscreenContext.imageSmoothingEnabled).toBeFalsy() | ||
}) | ||
|
||
describe('.prototype.attach', () => { | ||
it('attaches the onscreen canvas to the provided element', () => { | ||
let jasmineContent = document.body.querySelector('#jasmine-content') | ||
|
||
layer.attach(jasmineContent) | ||
|
||
expect(jasmineContent.querySelector('canvas')).toExist() | ||
}) | ||
}) | ||
|
||
describe('.prototype.resetOffscreenSize', () => { | ||
it('sets the width of the offscreen canvas to the ', () => { | ||
layer.canvas.width = 500 | ||
layer.canvas.height = 400 | ||
|
||
expect(layer.offscreenCanvas.width).toEqual(100) | ||
expect(layer.offscreenCanvas.height).toEqual(300) | ||
|
||
layer.resetOffscreenSize() | ||
|
||
expect(layer.offscreenCanvas.width).toEqual(500) | ||
expect(layer.offscreenCanvas.height).toEqual(400) | ||
}) | ||
}) | ||
|
||
describe('.prototype.copyToOffscreen', () => { | ||
it('copies the onscreen bitmap onto the offscreen canvas', () => { | ||
spyOn(layer.offscreenContext, 'drawImage') | ||
|
||
layer.copyToOffscreen() | ||
|
||
expect(layer.offscreenContext.drawImage).toHaveBeenCalledWith(layer.canvas, 0, 0) | ||
}) | ||
}) | ||
|
||
describe('.prototype.copyFromOffscreen', () => { | ||
it('copies the offscreen bitmap onto the onscreen canvas', () => { | ||
spyOn(layer.context, 'drawImage') | ||
|
||
layer.copyFromOffscreen() | ||
|
||
expect(layer.context.drawImage).toHaveBeenCalledWith(layer.offscreenCanvas, 0, 0) | ||
}) | ||
}) | ||
|
||
describe('.prototype.copyPartFromOffscren', () => { | ||
it('copies to the onscreen canvas the region that were specified', () => { | ||
spyOn(layer.context, 'drawImage') | ||
|
||
layer.copyPartFromOffscreen(50, 100, 150) | ||
|
||
expect(layer.context.drawImage).toHaveBeenCalledWith( | ||
layer.offscreenCanvas, | ||
0, 50, 100, 150, | ||
0, 100, 100, 150 | ||
) | ||
}) | ||
}) | ||
|
||
describe('.prototype.clearCanvas', () => { | ||
it('clears the whole canvas region', () => { | ||
spyOn(layer.context, 'clearRect') | ||
|
||
layer.clearCanvas() | ||
|
||
expect(layer.context.clearRect).toHaveBeenCalledWith(0, 0, layer.canvas.width, layer.canvas.height) | ||
}) | ||
}) | ||
}) |