-
Notifications
You must be signed in to change notification settings - Fork 3.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[第 46 期] who runs china 可视化 #333
Comments
well done! 好歹也应该有个中文吧 |
哇这也太美了吧,您好请问能讲讲是怎么实现的吗,好想学习呀! |
看页面源码是用D3配合插件完成 |
实现方式简介一、展现方式:分为动画层和交互层。 动画层主要用canvas来渲染粒子的运动。性能较svg优异。 交互层用d3和svg来绘制粒子静止时的图表,绑定交互事件方便,也可以用css来设置交互样式和交互动画。编码方便。 二、流程控制:主要有两个:页面滚动事件控制以及fsm渲染控制。 1.页面滚动事件控制页面滚动用到了storytelling神器 scrollama.js 库。 2.自定义的Fsm渲染控制:fsm有限状态机简单概念可以参考这篇文章 。 // 循环更新fsm
function renderScene(timestamp) {
fsm.update(timestamp);
requestAnimationFrame(renderScene);
}
renderScene();
// fsm 主流程
fsm = {
current: null,
next: null,
update: function (timestamp) {
if (fsm.next) {
var change = function () {
fsm.current = fsm.next;
fsm.next = null;
stages[fsm.current].enter();
stages[fsm.current].update(timestamp);
};
if (fsm.current) {
stages[fsm.current].leave(change);
} else {
change();
}
} else if (fsm.current) {
stages[fsm.current].update(timestamp);
}
},
trans: function (next) {
if (fsm.current !== next) {
fsm.next = next;
}
},
endTrans: function () {
var endTrans = stages[fsm.current].endTrans;
if (endTrans) {
endTrans();
}
}
};
// fsm状态, 以下为random状态示例
var stages = window.stages = {
'random': {
stage: 'random',
enter: function () {
this.isFinished = false;
this.start = null;
this.duration = durationTime;
var stage = this.stage;
var duration = this.duration;
var r = 3;
function getRandomColor() {
var letters = '0123456789ABCDEF';
var color = '#';
for (var i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
// compute v
data.forEach(function (d, i) {
var stagePosition = positions[stage];
d.position = {
startX: d.position.x,
startY: d.position.y,
endX: stagePosition[i].x,
endY: stagePosition[i].y,
x: d.position.x,
y: d.position.y,
vx: (stagePosition[i].x - d.position.x) / duration,
vy: (stagePosition[i].y - d.position.y) / duration,
r: r,
color: d.randomColor || d.position.color || getRandomColor(),
index: i
};
d.randomColor = d.randomColor || d.position.color;
});
},
update: function (timestamp) {
if (this.isFinished) {
return;
}
if (!this.start) this.start = timestamp;
var progress = timestamp - this.start;
var duration = this.duration;
// console.log(progress, timestamp, duration, durationTime);
if (progress >= duration) {
this.isFinished = true;
}
var setData = function () {
data.forEach(function (item, i) {
var d = item.position;
if (progress >= duration) {
d.x = d.endX;
d.y = d.endY;
} else {
d.x = d.startX + d.vx * progress;
d.y = d.startY + d.vy * progress;
}
});
};
var draw = function () {
ctx.clearRect(0, 0, canvas.width, canvas.height);
data.forEach(function (item, i) {
var d = item.position;
ctx.beginPath();
// arc(x, y, radius, startAngle, endAngle, anticlockwise)
ctx.arc(d.x, d.y, d.r, 0, 2 * Math.PI, false);
ctx.fillStyle = d.color;
ctx.fill();
ctx.closePath();
});
if (progress >= duration && (!isMobile.any || recentDeputy !== null)) {
stages[fsm.current].endTrans();
}
};
setData();
draw();
},
endTrans: function () {
if (!stages[fsm.current].isFinished) {
return;
}
setTimeout(function () {
$mysvg.show();
mysvgCircle
.attr('cx', function (d, i) { return d.position.x; })
.attr('cy', function (d, i) { return d.position.y; })
.attr('r', function (d, i) {
var r = recentDeputy === d.index ? 5 : d.position.r;
if (recentDeputy === d.index) {
console.log(i, d);
console.log(r);
}
return r;
})
.attr('fill', function (d, i) { return recentDeputy === d.index ? highlightColor : d.position.color; })
.classed('active', function (d) { return recentDeputy === d.index; })
if (!isMobile.any) {
mysvgCircle.on('mouseover', null);
mysvgCircle.on('mouseover', function (d, i) {
showPerson(d);
});
mysvgCircle.on('mouseout', hidePerson);
}
}, 0);
},
leave: function (cb) {
$mysvg.hide();
cb && cb();
},
isFinished: false
},
....
}
三、布局算法1. 屏幕适配。项目采用了scaleToWindow这个库可以把一个div缩放,撑满屏幕,效果类似于background 图片的position设置为contain。所以我们只做了PC、移动2种固定的分辨率,然后缩放。极大地简化了布局算法的编写。 2. 文字粒子的定位我们的设计师很认真地画出了文字粒子的AI图。AI图导出成svg, 就有了各个圆的位置、大小和颜色。通过解析svg文本文件,就得到了文字粒子的定位数据。 3. 常规图表的定位这个是自己编写的。设计稿中有图例,数字,根据这些图例和数据,手工调整了每一群粒子的位置。 感谢下面的示例,给了不少启发: |
好的,非常感谢大佬的回复,我学习一下。之前我学了canvas,但是用的不多都快忘完了,得好好复习复习了,哈哈。 |
so cool, 而且很用心 |
都干这一行了,要么学英文,要么就安心享用二手的中文资料吧 |
做的非常棒,优秀👍 |
@jdk137 大佬可以开源代码吗? |
版权不是我的。但网页即源码。 |
好**酷炫!! |
非常棒👍 |
您好,我想问一下关于安卓端的谷歌浏览器不兼容 scrollama.js 是如何解决的 |
最近忙于开发人大代表数据可视化,可以了解中国决策者们的人群特征和个人公开信息,在国内传媒届反响不错。 https://news.cgtn.com/event/2019/whorunschina/index.html
The text was updated successfully, but these errors were encountered: