Skip to content

Commit

Permalink
Add a CanvasLayer class to handle onscreen/offscreen canvases
Browse files Browse the repository at this point in the history
  • Loading branch information
abe33 committed Dec 9, 2015
1 parent 2b881c5 commit b339e73
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 0 deletions.
72 changes: 72 additions & 0 deletions lib/canvas-layer.js
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)
}
}
100 changes: 100 additions & 0 deletions spec/canvas-layer-spec.js
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)
})
})
})

0 comments on commit b339e73

Please sign in to comment.