首先为大家简单介绍一下这两种方法的工作原理,SVG是在XML中描述二维图形,所以在SVG DOM中的每一个元素都是可用的,你可以为每一个元素增加JS事件处理器。而Canvas是在fly上绘画2D图形,也是使用JS事件处理器,它的绘画能够按照像素重新生成。除此之外,在SVG中,每个图形都会被标记,只要SVG对象属性发生改变,浏览器就会自动重新生成相对应的新图像了,而在Canvas中,一旦图形完成,就会被遗忘,不会被标记,更不用说根据目标对象属性的改变而重新生成新图像了。
第二点,SVG和Canvas的一些其他区别。首先在分辨率上,Canvas是非常依赖图形的分辨率的,这也就限制了我们对于图形的选择,导致大家的实际 *** 作出现很多的麻烦和困难,而SVG对于分辨率就不是那么依赖了,对于图形的选择更加自由。其次,SVG还能支持事件处理器,相对于不能支持事件处理器的Canvas来说,也是十分方便的。最后,在应用方面,由于SVG复杂高度的渲染能力使得它适合带有大型渲染区域的应用程序,例如谷歌地图,而Canvas它的文本渲染能力很弱,渲染速度快,最适合图像密集型的游戏。
最近因为工作需要做了点HTML5可视化研究如下
为什么没有字体颜色作为一个设计师很不爽
第一次发文有写错的地方还请诸位高手多多包涵
设想,要在页面中生成大量的简单图形,比如10万个方块,并对它们进行拖拽 *** 作。
基本思路有三种方式,传统Div,Svg,与Canvas。
为了加拖拽,暂时没有用canvas。
因为canvas无法生成dom节点,不存在id这种属性,需要通过判断鼠标位置来获取元素再进行 *** 作。虽然写个isMouseinObj()的function也不是不行,但总觉得以后针对某对象单独处理会夜长梦多(主要还是懒)。。。不过从生成图形角度讲,canvas理论上是最快的。
因为贪生怕死担心Dom过多死机,准备先分区生成方块,于是:
一个简图,整体划分如下,设两个input框的值分别为m, n,每个蓝块包括n个绿块。点击每个蓝块可在下方生成n个绿块。
点击GenAll可一次性生成m*n个绿块。点击Drag可对绿块进行拖拽。
模拟开始。
首先是用div模拟方块。因为比较熟悉写起来也简单。
Div的拖拽方式可以分成两种。
Jquery-ui的draggble还有很多其他参数,请参考api文档。
现在假定一种新情形:绿色方块只能拖拽到虚线框之内。
默认情况下,Html元素均不可拖拽,所以需要设置拖拽元素的draggable属性为true。同时,默认无法将元素放置到其他元素中,所以需要event.preventDefault()设置允许放置。
本例中,为class为abox的绿方块添加draggable。
为class为wrap的虚线框添加preventDefault。
之后通过dataTransfer传输数据,实现box的移动。
由于Svg也是直接在Html中生成Dom节点,理论上Div所能实现的功能它都可以实现,并且绘图效果更佳。
然后生成Svg元素,并设置其属性。
循环生成Svg方块并添加拖拽属性。
但这种方式有个问题,就是——慢。当生成仅10000个方块时,效率便低的不可估量。
然而Svg无法应用Html5原生的Drag and Drop事件。
相比于引用插件,这样的效率提高了不少。
从结果上看,在数量少时,针对方块这种简单图形的简单 *** 作Div和Svg均可胜任。然而设置了总共生成100000个方块,发现单从生成的角度,Svg的渲染用时大约是Div的1/2(这里指Dom中直接绘制Svg而非通过js插件绘制Svg)。
加上拖拽功能后,用Html5原生拖放事件的Div,及用鼠标事件的Svg,明显快快快快于应用js插件拖放的效率。于是乎插件虽然强大但对于大量节点的处理实在过于缓慢。
于是在图形化上还是应用Svg更舒畅一些。
但有一个尚未解决的问题。
应用鼠标事件拖动Svg,当鼠标移动过快时,mousemove事件无法触发,导致移动效果不能实现。粗略查了下似乎可以添加透明背景层接收所有触发事件,不过还没有深入研究。
这篇就到此吧。等解决了mousemove的bug再更新后续。
(´・ω・`)
DOM是Web前端领域非常重要的组成部分,不仅在处理HTML元素时会用到DOM,图形编程也同样会用到。比如SVG绘图,各种图形都是以DOM节点的形式插入到页面中,这就意味着可以使用DOM方法对图形进行 *** 作。比如有一个 元素,可以直接用jquery增加click事件$('#p1').click(function(){…})"。然而这种DOM处理方法在HTML5的Canvas里不再适用,Canvas使用的是另外一套机制,无论在Canvas上绘制多少图形,Canvas都是一个整体,图形本身实际都是Canvas的一部分,不可单独获取,所以也就无法直接给某个图形增加JavaScript事件。
Canvas的限制
在Canvas里,所有图形都绘制在帧上,绘制方法不会将绘制好的图形元素作为一个返回值输出,js也无法获取到已经绘制好的图形元素。比如:
代码如下:
cvs = document.getElementById('mycanvas')
ctx = canvas.getContext('2d')
theRect = ctx.rect(10, 10, 100, 100)
ctx.stroke()
console.log(theRect)//undefined
这段代码在canvas标签里绘制了一个矩形,首先可以看到绘制图形的rect方法没有返回值。如果打开浏览器的开发者工具,还可以看到canvas标签内部没有增加任何内容,而在js里获取到的canvas元素以及当前的上下文,也都没有任何表示新增图形的内容。
所以,前端常用的dom方法在canvas里是不适用的。比如点击上面Canvas里的矩形,实际点击的是整个Canvas元素。
给Canvas元素绑定事件
由于事件只能达到Canvas元素这一层,所以,如果想进一步深入,识别点击发生在Canvas内部的`哪一个图形上,就需要增加代码来进行处理。基本思路是:给Canvas元素绑定事件,当事件发生时,检查事件对象的位置,然后检查哪些图形覆盖了该位置。比如上面的例子里画过一个矩形,该矩形覆盖x轴10-110、y轴10-110的范围。只要鼠标点击在这个范围里,就可以视为点击了该矩形,也就可以手动触发矩形需要处理的点击事件。思路其实比较简单,但是实现起来还是稍微有点复杂。不仅要考虑这个判断过程的效率,有些地方还需要重新判断事件类型,设置要重新定义一个Canvas内部的捕获和冒泡机制。
首先要做的,是给Canvas元素绑定事件,比如Canvas内部某个图形要绑定点击事件,就需要通过Canvas元素代理该事件:
代码如下:
cvs = document.getElementById('mycanvas')
cvs.addEventListener('click', function(e){
//...
}, false)
接下来需要判断事件对象发生的位置,事件对象e的layerX和layerY属性表示Canvas内部坐标系中的坐标。但是这个属性Opera不支持,Safari也打算移除,所以要做一些兼容写法:
代码如下:
function getEventPosition(ev){
var x, y
if (ev.layerX || ev.layerX == 0) {
x = ev.layerX
y = ev.layerY
} else if (ev.offsetX || ev.offsetX == 0) { // Opera
x = ev.offsetX
y = ev.offsetY
}
return {x: x, y: y}
}
//注:使用上面这个函数,需要给Canvas元素的position设为absolute。
现在有了事件对象的坐标位置,下面就要判断Canvas里的图形,有哪些覆盖了这个坐标。
isPointInPath方法
Canvas的isPointInPath方法可以判断当前上下文的图形是否覆盖了某个坐标,比如:
代码如下:
cvs = document.getElementById('mycanvas')
ctx = canvas.getContext('2d')
ctx.rect(10, 10, 100, 100)
ctx.stroke()
ctx.isPointInPath(50, 50)//true
ctx.isPointInPath(5, 5)//false
接下来增加一个事件判断,就可以判断一个点击事件是否发生在矩形上:
代码如下:
cvs.addEventListener('click', function(e){
p = getEventPosition(e)
if(ctx.isPointInPath(p.x, p.y)){
//点击了矩形
}
}, false)
以上就是处理Canvas事件的基本方法,但是上面的代码还有局限,由于isPointInPath方法仅判断当前上下文环境中的路径,所以当Canvas里已经绘制了多个图形时,仅能以最后一个图形的上下文环境来判断事件,比如:
代码如下:
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)