Shane Xu's Home

Life is too short for so much sorrow.

如何用 N 张图片拼出 2016 字样

如何用 N 张图片拼出 2016 字样。这是我几年前,在公司年会上的抽奖程序里面实现的一个效果。

我那时候的想法很简单,就是把所有抽奖人的头像,排列成“2015”的字样,今天我排的是 2016。最后实现的效果就是下面这样的。

一开始有这个想法的时候,我并没有解决方案。我那个时候在网上,搜索了半圈,也没找到靠谱的方案,甚至几乎连类似的效果都没有人提及。于是我自己思索。

  1. 计算机上的文字本身是如何显示的,在一个方格 AxB 大小的方格里,通过字体文件,计算机知道方格中的某个像素应该染上颜色。
  2. 如果在 div 的区域内,知道该在哪个像素点填充颜色,就能“画出”文字。
  3. 将文字画在图片上,通过获取图像上每个像素点的颜色就知道,该坐标是否需要染色。
  4. 前端 js 有方法画出文字吗?
  5. 有 canvas。
  6. 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: );

Comments

comments powered by Disqus