Last active
June 17, 2016 17:31
-
-
Save danschumann/cb832f6341aa697e58cb to your computer and use it in GitHub Desktop.
html canvas text addons
This file contains hidden or 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
Canvas = require('canvas') | |
_ = require('underscore') | |
Canvas.Context2d::fillTextCircle = (text, x, y, radius, letterSpacing, angle, reverse, letterSpacingFlat, maxAngle) -> | |
if _.isObject text | |
{ | |
text | |
x | |
y | |
radius | |
letterSpacing | |
angle | |
reverse | |
letterSpacingFlat | |
maxAngle | |
getAngle | |
} = text | |
prevWidth = null | |
#@textAlign = 'center' | |
fontSize = parseFloat (''+@font).replace(/[^\d\.]*/, '') | |
circumference = radius * 2 * Math.PI | |
angles = [] | |
letterSpan = 0 | |
# Loop once for all angles and total | |
totalAngle = 0 | |
n = -1 | |
while ++n < text.length | |
char = text[n] | |
thisWidth = @measureText(char).width | |
angles[n] = unless prevWidth? then 0 else ( | |
( | |
Math.pow((thisWidth + prevWidth - 3)/ circumference * .5, .45) * letterSpacing - letterSpacingFlat | |
) | |
) * (reverse && -1 || 1) | |
prevWidth = @measureText(char).width | |
totalAngle += angles[n] | |
totalAngle += switch _.last text | |
when 'o','N', 'Y' | |
.010 | |
when 'u', 'h','n','M',"U" | |
.015 | |
when 'i', 'l', 'q' | |
.02 | |
when 'g','I' | |
.025 | |
when 'm' | |
.035 | |
else 0 | |
# Sometimes we only want the total for computational purposes | |
return totalAngle if getAngle | |
@save() | |
@translate x, y | |
# Move their specified angle to center text around it | |
angle -= totalAngle / 2 #center | |
@textAlign = 'center' | |
n = -1 | |
angleOffset = 0 | |
while ++n < text.length | |
@save() | |
char = text[n] | |
# Last char gets .0000001 | |
@rotate angle + angleOffset + angles[n] + (if n<text.length-1 | |
switch char | |
when 'b' | |
.004 | |
when 'f' | |
.007 | |
when 't' | |
.007 | |
when 'q' | |
-.004 | |
when 'r' | |
.008 | |
when 'w' | |
.015 | |
when 'j' | |
.005 | |
when 'v' | |
.015 | |
when 'y' | |
.01 | |
when 'V' | |
.011 | |
when 'P' | |
.004 | |
when 'O' | |
.007 | |
when 'T' | |
.006 | |
when 'W' | |
.005 | |
when 'Y' | |
.005 | |
when 'R' | |
.006 | |
when 'A' | |
.004 | |
when 'h' | |
.006 | |
else 0 | |
else 0 | |
) * (if reverse then -1 else 1) | |
if maxAngle and angles[n] > maxAngle | |
@restore() | |
break | |
else | |
@fillText char, 0, (if reverse then fontSize/2 + radius else -radius) | |
angleOffset += angles[n] | |
@restore() | |
@restore() | |
{totalAngle} | |
# Since we might require this file multiple times we have to copy the original only once | |
global._fillText ?= Canvas.Context2d::fillText | |
Canvas.Context2d::fillText = (text, x, y, args...) -> | |
# no kerning? default behavior | |
return global._fillText.apply this, arguments unless @kerning? | |
# we need to remember some stuff as we loop | |
offset = 0 | |
_.each text, (letter) => | |
global._fillText.apply this, [ | |
letter | |
x + offset + @kerning | |
y | |
].concat args # in case any additional args get sent to fillText at any time | |
offset += @measureText(letter).width + @kerning | |
return (app) => | |
canvas = new Canvas(500, 500) | |
ctx = canvas.getContext('2d') | |
ctx.font = '30px Impact' | |
ctx.rotate(.2) | |
ctx.fillText("lorem ipsum", 50, 100) | |
_.defer => app.get '/logo.png', (req, res) -> | |
res.writeHead(200,'image/jpg') | |
imgCanv = canvas.createJPGStream() | |
imgCanv.on "error", (exception)-> res.send 'error' | |
imgCanv.on "data", (data)-> res.write data | |
imgCanv.on "end", -> res.end('binary') | |
serveCanvas = (res, canvas) -> | |
res.type "image/png" | |
nodefn.call( _.bind(canvas.toBuffer, canvas) ) | |
.then (buffer) -> | |
res.end buffer | |
.otherwise (er) -> | |
console.log "ERROR!".red, er, er.stack if er | |
filterText: -> | |
# Filter out all characters that break canvas | |
if ttext = @props.params.ttext | |
ttext unless _.isString ttext | |
len = ttext.length | |
for i in [0...len] | |
if 760 < ttext.charCodeAt len - i | |
ttext = @props.params.ttext = ttext.slice(0, len - i) + ttext.slice len - i + 1 | |
there are some other methods mixed on, like a kerning addon for fillText, an example of how to write canvas to the response, and a filter so that canvas doesn't crash the server if someone passes sigart to it.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
eventually i could clean this up and put in a module. for now its just a method that writes text rotating around a circle. it's only been tested in arial.