Skip to content

Instantly share code, notes, and snippets.

@pc035860
Created April 11, 2013 13:37
Show Gist options
  • Save pc035860/5363432 to your computer and use it in GitHub Desktop.
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
<div id="stage">
<div id="char"></div>
<div id="wrap">
<canvas id="main" width="900" height="400"></canvas>
</div>
</div>
<div id="debug"></div>
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;
}
#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