pixi如何实现绘画功能
如何应用原生canvas api替代PIXI.Graphics来实现该功能 |
背景
至于为什么会有这样的问题,起始于我一次业务开发需要实现画板功能,当做到橡皮擦功能的时候,遇到了瓶颈。原生canvas的2d环境,支持 globalCompositeOperation 属性。 这一属性决定了后续的动作是绘制(source-over)还是擦除(destination-out)。
而pixi默认是使用WebGL环境的,哪怕是用了canvas环境,也无法这样做,因为我的画板背后还有背景图案,当然还有一些其他的限制。例如PIXI.Graphics本身属于一个PIXI.Container,自身维护的数据比较多,占用内存较高。并且Container的特点是尺寸会随着内容增大,这也导致后期截取数据较为麻烦。
因此,我就在想,能不能有方法同时使用原生canvas的pixi。当然最终的解决方法也并非我另辟蹊径,只是通过反复查看官方的api文档发现了之前遗漏的功能而已。
理解PIXI.Texture.from
其实仔细看第一个参数source的Type可以发现,source可以是字符串、img标签、canvas标签、video标签,以及PIXI.BaseTexture。而我们参照一般文档进行开发,使用最多的其实是PIXI.BaseTexture。因为我们通过Loader加载的图片,默认都创建成了BaseTexture,我们new PIXI.Sprite(texture)
的时候,texture就是它。而string类型,我想new PIXI.Text(str)
的时候就是了。这里既然提到了canvas,那我们是不是可以尝试用一个canvas标签做真正的绘图,然后再通过pixi的WebGL环境实时展示?
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108
| <!DOCTYPE html> <html lang="en">
<head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>画图</title> <script src="https://cdn.bootcdn.net/ajax/libs/pixi.js/5.3.10/pixi.min.js"></script> </head>
<body> <div id="box"></div> <button id="add">add</button> <button id="del">del</button> <script> let app = new PIXI.Application({ width: 400, height: 300, backgroundColor: 0xcccccc }); document.getElementById('box').appendChild(app.view);
let canvas = document.createElement('canvas') canvas.width = app.renderer.width canvas.height = app.renderer.height canvas.style.background = '#aaaaaa' let ctx = canvas.getContext('2d') document.getElementById('box').appendChild(canvas);
let tx = PIXI.Texture.from(canvas) let sp = new PIXI.Sprite(tx) sp.name = 'preview' sp.width = app.renderer.width sp.height = app.renderer.height sp.hitArea = new PIXI.Rectangle(0, 0, app.renderer.width, app.renderer.height) sp.zIndex = 2 sp.interactive = true app.stage.addChild(sp);
let mode = 'add' let lastPoint = { x: 0, y: 0 }; let drawing = false;
document.getElementById('add').onclick = () => mode = 'add' document.getElementById('del').onclick = () => mode = 'del'
sp.on('mousedown', drawStart); sp.on('mouseup', drawEnd); sp.on('mouseout', drawEnd); sp.on('mousemove', drawMove);
function drawStart(event) { let r = Math.random() * 1, g = Math.random() * 1, b = Math.random() * 1, a = Math.random() * 1
if (mode === 'add') { ctx.lineWidth = 7; ctx.globalCompositeOperation = "source-over" } else { ctx.lineWidth = 21; ctx.globalCompositeOperation = "destination-out" }
ctx.lineCap = 'round'; ctx.lineJoin = 'round'; ctx.strokeStyle = `rgba(${r * 255},${g * 255},${b * 255},1)` let { x, y } = event.global;
lastPoint = { x, y }; drawing = true; } function drawMove(event) { if (drawing == true) {
let { x, y } = event.global;
ctx.beginPath(); ctx.moveTo(lastPoint.x, lastPoint.y); ctx.lineTo(x, y); ctx.stroke() tx.update()
lastPoint = { x, y }; } }
function drawEnd() { drawing = false; } </script> </body>
</html> </script> </body>
</html>
|
DEMO
两个canvas,前一个是pixi的舞台,使用WebGL。后一个是原生canvas,使用2d环境绘制。
前一个接收交互事件,计算绘图的坐标。
然后交给canvas的2d环境,使用原生api进行绘图
最后update,前者就会更新显示
更新记录
2023-02-13
新版本api更改,pixijs的sprite对象交互事件里不再有getLocalPosition方法,而是在global里即可获取实时位置
1 2
| // let { x, y } = event.data.getLocalPosition(this.parent); //获取鼠标移动的位置 let { x, y } = event.global; //获取鼠标移动的位置
|