Created
September 27, 2015 05:27
-
-
Save ConradIrwin/e6b3d65beab92f1ce19d to your computer and use it in GitHub Desktop.
Example canvas icon for canvas-animation-loader
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
d3 = require 'd3' | |
module.exports = class SearchIcon | |
# Construct an instance of a search icon. | |
# The animation will take {duration} milliseconds, and occupy | |
# a grid {width}x{height} pixels. | |
constructor: (duration, width, height) -> | |
if width != height | |
throw new ArgumentError("search icon was not rendered in a square") | |
@_scale = d3.scale.linear().domain([0, width]).range([0, 32]) | |
# the domain of the speed function is number of milliseconds | |
# after the start of the transition. | |
# the range is normalized from 0 to 1. | |
@_speed = d3.scale.linear().domain([0, duration]).range([0, 1]) | |
# Apply sinusoidal easing. | |
@_ease = d3.ease('sin-in-out') | |
# each of these scales handles the animation of part of the icon. | |
# the domain (input range) is over time, from 0 to 1. In other words an | |
# animation with domain [0, 0.5] would only move for the first half of time. | |
# the range is the range of output values, usually pixel positions. | |
# The handleStartPosition is the extension of the magnifying glass handle into the full cross-bar. | |
_handleStartPosition: d3.scale.linear().domain([0, 0.55]).range([1, 20]).clamp(true) | |
# The tailDegree is the back of the arc as it unwinds around the magnifying glass head | |
_tailDegreeScale: d3.scale.linear().domain([0.188, 1]).range([0, Math.PI * 2]).clamp(true) | |
# the arcPosition is the vertical co-ordinate of the arc as it opens up into an X | |
_arcPositionScale: d3.scale.linear().domain([0, 0.188]).range([31, 11.5]).clamp(true) | |
# the headDegree is the front of the arc as it coils from the magnifying glass to straight | |
_headDegreeScale: d3.scale.linear().domain([0.446, 1.05]).range([0.25, 1]).clamp(true) | |
# the topBarLength is the extrusion of the top-right bar of the X | |
_topBarLengthScale: d3.scale.linear().domain([0, 0.446]).range([15, 0]).clamp(true) | |
# render the icon to the given canvas context at the given time. | |
# ensure: 0 <= timeInMilliseconds <= duration | |
render: (ctx, timeInMilliseconds) => | |
tDirection = d3.scale.linear().domain([0, 1]).range([0.001, 1]).clamp(true) | |
t = tDirection(@_ease(@_speed(timeInMilliseconds))) | |
ctx.clearRect @_scale(0), @_scale(0), @_scale(32), @_scale(32) | |
ctx.strokeStyle = 'rgb(79, 79, 79)' | |
ctx.lineWidth = @_scale(2.5) | |
if t == 0 | |
# draw a cross. | |
ctx.beginPath() | |
ctx.moveTo @_scale(1), @_scale(1) | |
ctx.lineTo @_scale(31), @_scale(31) | |
ctx.moveTo @_scale(31), @_scale(1) | |
ctx.lineTo @_scale(1), @_scale(31) | |
ctx.stroke() | |
return true | |
else if t == 1 | |
# draw a magnifying glass. | |
ctx.beginPath() | |
ctx.moveTo @_scale(31), @_scale(31) | |
ctx.lineTo @_scale(20), @_scale(20) | |
ctx.stroke() | |
ctx.beginPath() | |
ctx.arc @_scale(12.25), @_scale(12.25), @_scale(10.75), 0, Math.PI * 2 | |
ctx.stroke() | |
return true | |
else | |
handleStart = @_handleStartPosition(t) | |
# move the tail for half the time | |
tailDegree = @_tailDegreeScale(t) | |
headDegree = @_headDegreeScale(t) | |
topBarLength = @_topBarLengthScale(t) | |
arcPosition = @_arcPositionScale(t) | |
# we want an arc that is tangent to the cross' top-right bar, and which | |
# starts at the current arcPosition. | |
center = (520 - (arcPosition * arcPosition)) / (62 - (2 * arcPosition)) | |
# hack, the line rendering fails for ridiculously tiny numbers | |
center = Math.max(-10000, center) | |
radius = Math.sqrt(2 * (16 - center) ** 2) | |
initialDegree = Math.PI / 2 - Math.atan((1 - center) / (arcPosition - center)) | |
# the magnifying glass handle <-> the top-left, bottom-right line on the cross | |
ctx.beginPath() | |
ctx.moveTo @_scale(31), @_scale(31) | |
ctx.lineTo @_scale(handleStart), @_scale(handleStart) | |
ctx.stroke() | |
# the magnifying glass head | |
ctx.beginPath() | |
ctx.arc @_scale(12.25), @_scale(12.25), @_scale(10.75), 1 * Math.PI, tailDegree + 1 * Math.PI | |
ctx.stroke() | |
# the arc from the magnifying glass head to the top-right bar | |
ctx.beginPath() | |
ctx.arc @_scale(center), @_scale(center), @_scale(radius), Math.max(initialDegree, headDegree * Math.PI), headDegree * Math.PI, true | |
ctx.stroke() | |
# the top-right bar | |
ctx.beginPath() | |
ctx.moveTo @_scale(16), @_scale(16) | |
ctx.lineTo @_scale(16 + topBarLength), @_scale(16 - topBarLength) | |
ctx.stroke() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment