console
var TextFlow = function(args) {
this.stop = false;
this.canvas = args.canvas;
this.ctx = args.ctx;
this.rawPoints = args.points;
this.data = new TextFlowData();
this.points = this.data.getPoints.bind(this.data);
this.fadeInStyle = args.fadeInStyle || "rgba(0,0,0, 0)";
this.fadeOutStyle = args.fadeOutStyle || "rgba(0,0,0, 1)";
this.ctx.font = args.fontStyle || '20px normal';
this.addPoints(this.rawPoints);
}
TextFlow.prototype.addPoints = function(rawPoints) {
this._preprocessingPoints(rawPoints);
this.data.addPoints(rawPoints);
this._checkPointsAndStop();
}
TextFlow.prototype.start = function() {
window.requestAnimationFrame(this._animate.bind(this));
}
TextFlow.prototype.stopAnimate = function() {
this.stop = true;
}
TextFlow.prototype._checkPointsAndStop = function() {
var points = this.points();
if (points.length === 0) {
this.stop = true;
} else {
if (this.stop) {
this.start();
}
this.stop = false;
}
}
TextFlow.prototype._preprocessingPoints = function(rawPoints) {
var lineHeight = (this.canvas && parseInt(window.getComputedStyle(this.canvas).lineHeight)) || parseInt(window.getComputedStyle(document.body).lineHeight);
for(var i = 0; i < rawPoints.length; i++) {
var str = rawPoints[i].text;
var text = str.split('');
var textMetrics = this.ctx.measureText(str);
var textWidth = textMetrics.width / text.length;
var textHeight = text.length * lineHeight;
rawPoints[i].lh = lineHeight;
rawPoints[i].th = textHeight;
}
}
TextFlow.prototype._animate = function(item) {
if (!this.stop) {
this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height);
this._draw();
this._update();
window.requestAnimationFrame(this._animate.bind(this));;
}
}
TextFlow.prototype._draw = function() {
var points = this.points();
this.ctx.save();
for(var i = 0; i < points.length; i++) {
var point = points[i];
this._fillVerticalText(point.text, point.x, point.y, point.ys, point.lh, point.th);
}
this.ctx.restore();
}
TextFlow.prototype._update = function() {
var points = this.points();
for(var i = points.length - 1; i >= 0; i--) {
var point = points[i];
point.y = point.y - point.v;
if (point.y < point.ys - point.th * 3) {
this.data.reducePointByIndex(i);
}
}
this._checkPointsAndStop();
}
TextFlow.prototype._fillVerticalText = function(str, x, y, ys, lh, th) {
var text = str.split('');
if (y < ys - th) {
this.ctx.fillStyle = this._createColor(x, ys - th, x, ys - th * 2, true);
} else {
this.ctx.fillStyle = this._createColor(x, ys, x, ys - th);
}
for (var i = 0; i < text.length; i++) {
y = Math.ceil(y + lh);
this.ctx.fillText(text[i], x, y);
}
}
TextFlow.prototype._createColor = function(x1, y1, x2, y2, fadeOut) {
var gradient = this.ctx.createLinearGradient(x1, y1, x2, y2);
if (fadeOut) {
gradient.addColorStop(0, this.fadeOutStyle);
gradient.addColorStop(1, this.fadeInStyle);
} else {
gradient.addColorStop(0, this.fadeInStyle);
gradient.addColorStop(1, this.fadeOutStyle);
}
return gradient;
}
var TextFlowData = function(args) {
this.points = [];
this.pointsPool = [];
this.maxPoint = (args && args.maxPoint) || 100;
this.maxPointsPool = (args && args.maxPointsPool) || 500;
}
TextFlowData.prototype.getPoints = function() {
return this.points;
}
TextFlowData.prototype.addPoints = function(rawPoints) {
this._addPointPool(rawPoints);
this._shiftPointPool();
}
TextFlowData.prototype.reducePointByIndex = function(i) {
this.points.splice(i, 1);
}
TextFlowData.prototype._createPoints = function(rawPoints) {
var resPoints = [];
for(var i = 0; i < rawPoints.length; i++) {
var point = rawPoints[i],
aPoint = {
text: point.text,
x: point.x,
y: point.y,
ys: point.y,
lh: point.lh,
th: point.th,
v: Math.floor(Math.random() * 3) + 1
};
resPoints.push(aPoint);
}
return resPoints;
}
TextFlowData.prototype._addPointPool = function(rawPoints) {
if (rawPoints.length > this.maxPointsPool) {
var reduceCount = rawPoints.length - this.maxPointsPool;
rawPoints.splice(0, reduceCount);
this.pointsPool.splice(0);
} else if (rawPoints.length + this.pointsPool.length >= this.maxPointsPool) {
var addCount = this.maxPointsPool - rawPoints.length;
this.pointsPool.splice(0, addCount);
}
var addPoints = this._createPoints(rawPoints);
this.pointsPool = this.pointsPool.concat(addPoints);
}
TextFlowData.prototype._shiftPointPool = function() {
var addCount = this.maxPoint - this.points.length;
if (addCount <= 0) return;
this.points = this.points.concat(this.pointsPool.splice(0, addCount));
}
var canvas = document.getElementById('myCanvas'),
ctx = canvas.getContext('2d'),
points = [{
text: '123456',
x: 100,
y: 500
}];
initPoints();
function initPoints() {
var i = 100;
while(i > 0) {
initPoint();
i--;
}
}
function initPoint() {
var maxCount = 999999999;
maxX = canvas.width - 30,
maxY = canvas.height - 30,
point = {
text: Math.floor(Math.random() * maxCount) + '',
x: Math.floor(Math.random() * maxX),
y: Math.floor(Math.random() * maxY)
};
points.push(point);
}
console.info('points', points);
var textflow = new TextFlow({
canvas: canvas,
ctx: ctx,
points: points,
fadeInStyle: 'rgba(64, 64, 64, 0)',
fadeOutStyle: 'rgba(64, 64, 64, 1)'
})
textflow.start();
setInterval(function() {
points = [];
initPoints();
textflow.addPoints(points);
}, 5000)
<canvas id="myCanvas" width="500" height="400"></canvas>
canvas{
background-color: #eee;
line-height: 1.2;
}