Created
April 11, 2013 13:37
-
-
Save pc035860/5363432 to your computer and use it in GitHub Desktop.
A CodePen by Robin Fan. Move alone an Chart.js path - as title, draws by sampling from off-screen canvas
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
<div id="stage"> | |
<div id="char"></div> | |
<div id="wrap"> | |
<canvas id="main" width="900" height="400"></canvas> | |
</div> | |
</div> | |
<div id="debug"></div> |
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
window.requestAnimFrame = (function(){ | |
return window.requestAnimationFrame || | |
window.webkitRequestAnimationFrame || | |
window.mozRequestAnimationFrame || | |
function( callback ){ | |
window.setTimeout(callback, 1000 / 60); | |
}; | |
})(); | |
var data = { | |
labels: [], | |
datasets: [ | |
{ | |
strokeColor : "rgba(175, 215, 101, 1)", | |
pointColor : "rgba(255, 255, 255,1)", | |
pointStrokeColor : "rgba(185, 220, 120, 1)", | |
data : [] | |
} | |
] | |
}, | |
m_canvas = document.getElementById('main'), | |
m_ctx = m_canvas.getContext('2d'), | |
m_char = document.getElementById('char'), | |
m_wrap = document.getElementById('wrap'), | |
c_width = 900, | |
c_height = 400, | |
sample_dots, | |
sample_length, | |
sample_scale; | |
for (var i = 0; i < 22; i++) { | |
data.datasets[0].data.push(~~(Math.random() * 50)); | |
} | |
for (var i = 0; i < data.datasets[0].data.length; i++) { | |
data.labels.push(''); | |
} | |
sample_scale = 0.7; | |
(function () { | |
var fps = 60, | |
per_point_dur = 5 / data.datasets[0].data.length, | |
canvas = document.createElement('canvas'), | |
ctx; | |
canvas.width = c_width * sample_scale; | |
canvas.height = c_height * sample_scale; | |
ctx = canvas.getContext('2d'); | |
(new Chart(ctx)).Line(data, { | |
animation: false, | |
scaleShowGridLines: false, | |
datasetFill: false, | |
scaleLabel: ' ', | |
scaleLineColor: 'rgba(255, 255, 255, 0)', | |
pointDot: false, | |
onAnimationComplete: function () { | |
sample_dots = sampler( | |
ctx, | |
canvas.width, | |
canvas.height, | |
data.datasets[0].data.length * per_point_dur * fps | |
); | |
sample_length = sample_dots.length; | |
// sample_dots_bk = sample_dots.slice(0); | |
drawMainChart(); | |
animate(); | |
/* | |
m_char.addEventListener('click', function () { | |
sample_dots = sample_dots_bk.slice(0); | |
animate(); | |
}, false); | |
*/ | |
} | |
}); | |
})(); | |
function drawMainChart() { | |
data.datasets[0].fillColor = 'rgba(175, 215, 101, 0.3)'; | |
(new Chart(m_ctx)).Line(data, { | |
animation: false, | |
scaleShowGridLines: false, | |
datasetFill: true, | |
scaleLabel: ' ', | |
scaleLineColor: 'rgba(255, 255, 255, 0)', | |
pointDot: true | |
}); | |
} | |
var last_point = null; | |
function animate() { | |
if (sample_dots.length > 0) { | |
var p_buf = sample_dots.shift(); | |
if (p_buf) { | |
var point = p_buf, | |
rotate; | |
if (last_point === null) { | |
rotate = 0; | |
} | |
else { | |
//console.log((point[1] - last_point[1]) / 0.1); | |
rotate = Math.atan( | |
((point[1] - last_point[1]) / sample_scale) / (c_width / sample_length) | |
); | |
} | |
rotate = rotate || 0; | |
rotate = rotate / 5; | |
last_point = point.slice(0); | |
point[0] = (point[0] / sample_scale) / window.devicePixelRatio; | |
point[1] = (point[1] / sample_scale) / window.devicePixelRatio; | |
if (point[0] <= c_width && point[1] <= c_height) { | |
m_char.style.webkitTransform = 'translate('+point[0]+'px, '+point[1]+'px) rotate('+rotate+'rad)'; | |
//console.log(p_buf, point); | |
// m_char.style.webkitTransform = 'translate('+point[0]+'px, '+point[1]+'px)'; | |
// m_wrap.style.width = point[0] + 'px'; | |
} | |
} | |
requestAnimFrame(animate); | |
} | |
} | |
var sample_func = { | |
linear: function (t) { | |
return t; | |
}, | |
easeOutSine: function (t) { | |
return 1 * Math.sin(t/1 * (Math.PI/2)); | |
}, | |
easeOutQuart: function (t) { | |
return -1 * ((t=t/1-1)*t*t*t - 1); | |
} | |
}; | |
function sampler(ctx, width, height, p_number) { | |
var w = width, | |
h = height, | |
image_data = ctx.getImageData(0, 0, w, h), | |
buf = Array.prototype.slice.call(image_data.data), | |
key, | |
colored = [], | |
used_col = {}, | |
m_point, | |
itr_count = 1; | |
// find first non-transparent point | |
for (var i = 0; i < w; i++) { | |
for (var j = 0; j < h; j++) { | |
// key = '' + i + '-' + j; | |
var start = ((j*(w*4)) + (i*4)), | |
a_comp = buf[start + 3]; | |
if (a_comp != 0 /*&& !used_col[i]*/) { | |
used_col[i] = true; | |
colored.push([i, j]); | |
m_point = [i, j]; | |
break; | |
} | |
} | |
if (m_point) { | |
break; | |
} | |
} | |
// find by adjacent points | |
var found = false, | |
first_x = m_point[0]; | |
for (var k = first_x; k < w; k++) { | |
found = false; | |
// (x, y) = (col, row) | |
var range = h / 10, | |
col = m_point[0] + 1, | |
r_start = m_point[1] - range, | |
r_end = m_point[1] + range; | |
if (r_start < 0) { | |
r_start = 0; | |
} | |
if (r_end > w - 1) { | |
r_end = w - 1; | |
} | |
for (var row = r_start; row <= r_end; row++) { | |
var start = ((row*(w*4)) + (col*4)), | |
a_comp = buf[start + 3]; | |
itr_count++; | |
if (a_comp != 0 /*&& !used_col[col]*/) { | |
used_col[col] = true; | |
colored.push([col, row]); | |
var old = m_point.slice(0); | |
m_point = [col, row]; | |
found = true; | |
break; | |
} | |
} | |
if (!found) { | |
break; | |
} | |
} | |
$('#debug').html('sampling iteration: ' + itr_count + '<br>total pixel: ' + (c_width * c_height)); | |
var l = colored.length, | |
rate = l / p_number, | |
resampled = []; | |
for (var i = 0; i < p_number; i++) { | |
var s_p = i * rate, | |
eased = sample_func.linear(i / p_number) * (p_number / i) * s_p, | |
prev = ~~eased, | |
next = prev + 1, | |
prev_point, next_point, interpolated; | |
if (next >= l) { | |
resampled.push(colored[prev]); | |
} | |
else { | |
prev_point = colored[prev]; | |
next_point = colored[next]; | |
interpolated = [ | |
Number(prev_point[0]) + (eased - prev) * (Number(next_point[0]) - Number(prev_point[0])), | |
Number(prev_point[1]) + (eased - prev) * (Number(next_point[1]) - Number(prev_point[1])) | |
]; | |
resampled.push(interpolated); | |
} | |
} | |
return resampled; | |
} |
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
#stage { | |
position: relative; | |
} | |
#char { | |
cursor: pointer; | |
position: absolute; | |
margin: -20px -20px; | |
width: 40px; height: 40px; | |
background-image: url(https://graph.facebook.com/pc035860/picture?type=large); | |
background-size: cover; | |
/*-webkit-transition: all linear 10ms; */ | |
transform: translate(0px, 0px); | |
-webkit-transform: translate(0px, 0px); | |
border-radius: 20px; | |
-webkit-box-shadow: 0px 0px 3px 2px rgba(0, 85, 155, 0.5); | |
box-shadow: 0px 0px 3px 2px rgba(0, 85, 155, 0.5); | |
} | |
#wrap { | |
overflow: hidden; | |
width: 900px; | |
height: 400px; | |
} | |
#debug { | |
font-family: Consolas; | |
line-height: 1.5; | |
padding: 10px; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment