如何用 N 张图片拼出 2016 字样
如何用 N 张图片拼出 2016 字样。这是我几年前,在公司年会上的抽奖程序里面实现的一个效果。
我那时候的想法很简单,就是把所有抽奖人的头像,排列成“2015”的字样,今天我排的是 2016。最后实现的效果就是下面这样的。
一开始有这个想法的时候,我并没有解决方案。我那个时候在网上,搜索了半圈,也没找到靠谱的方案,甚至几乎连类似的效果都没有人提及。于是我自己思索。
- 计算机上的文字本身是如何显示的,在一个方格 AxB 大小的方格里,通过字体文件,计算机知道方格中的某个像素应该染上颜色。
- 如果在 div 的区域内,知道该在哪个像素点填充颜色,就能“画出”文字。
- 将文字画在图片上,通过获取图像上每个像素点的颜色就知道,该坐标是否需要染色。
- 前端 js 有方法画出文字吗?
- 有 canvas。
- canvas 里面如何获取每个点的颜色
var p = ctx.getImageData(i, j, 1, 1).data;
于是问题就解决了。首先,在需要放置图片的区域,先插入一个 canvas,然后在 canvas 的适当位置(这个位置,可以根据 canvas 的大小以及文字的大小进行计算,或者直接通过几次尝试), 用某种颜色(我选择蓝色),画上想要展示的文字,然后遍历 canvas 上每一个点,在有颜色的点上放置一张图片。这是最粗暴的做法。这样做不好的地方在于,放置图片太密集。因为图片本身 有大小,所以可以选择一定的步长来遍历图片上的像素点。
完整的代码如下。
1: var HiddenCanvas = React.createClass({ 2: getInitialState: function() { 3: return { 4: items: [] 5: }; 6: }, 7: componentDidMount: function() { 8: console.log(1); 9: var str = this.props.value; 10: var myCanvas = this.refs.myCanvas; 11: var ctx = myCanvas.getContext("2d"); 12: ctx.font="250px Droid Sans Mono"; 13: ctx.fillStyle="blue"; 14: ctx.fillText(this.props.value, 20, 250); 15: 16: function rgbToHex(r, g, b) { 17: if (r > 255 || g > 255 || b > 255) 18: throw "Invalid color component"; 19: return ((r << 16) | (g << 8) | b).toString(16); 20: } 21: 22: setTimeout(function(){ 23: var items = []; 24: var count = 0; 25: for(var i = 0; i < 800; i = i + 24) { 26: for(var j = 0; j < 300; j = j + 24) { 27: var p = ctx.getImageData(i, j, 1, 1).data; 28: var hex = "#" + ("000000" + rgbToHex(p[0], p[1], p[2])).slice(-6); 29: if(hex == "#0000ff") { 30: items.push({ 31: id: count++, 32: left: i - 12, 33: top: j - 12, 34: transform: 'rotate(' + (360 * Math.random()) + 'deg)' 35: }); 36: } 37: } 38: } 39: this.setState({ 40: items: items 41: }); 42: }.bind(this)); 43: }, 44: render: function() { 45: var images = this.state.items.map(function(m){ 46: return ( 47: <img key={m.id} src="https://avatars1.githubusercontent.com/u/1257453?v=3&s=460" style={{width: '48px', position: 'absolute', left: m.left, top: m.top, transform: m.transform}} /> 48: ); 49: }); 50: return ( 51: <div style={{position: 'relative'}}> 52: {images} 53: <canvas ref="myCanvas" width="800" height="300" style={{visibility: 'hidden'}}/> 54: </div> 55: ); 56: } 57: }); 58: 59: ReactDOM.render( 60: <HiddenCanvas value="2016"/>, 61: document.getElementById('example') 62: );