Canvas画图-鼠标移动图形

之前那篇Canvas画图-鼠标涂鸦已经可以实现与Canvas的简单交互,这篇会介绍Canvas中实现交互性的一个重要方法isPointInPath。

基本原理

Cavnas的确能实现很多很酷炫狂拽屌炸天的效果,但是交互性要差很多,简单的鼠标单击选中某个图形,都要花费一番周折。

Canvas只是一个dom节点,所有监听的事件都只能绑定在这个节点上,但是我们可能需要对Canvas上的某个元素进行操作。基本的原理就是事件还是绑定在Canvas上,通过判断点击发生的位置是否在Canvas中某个图形的路径内(这里注意,我没有说是某个图形的区域内),从而进一步确定是在哪个图形上戳来戳去。

isPointInPath

为了让我们判断点击点到底是否在路径内,Canvas提供了context.isPointInPath(x,y)方法,参数自然是某个点的横竖坐标。

来个例子,先随便画三个矩形:

        var rectList = [ [50,50,50,50], [120,120,80,60], [30,150,60,80] ];

        var drawRect = function(x,y,w,h){
            ctx.beginPath();
            ctx.rect(x,y,w,h);  
        }

        // init
        ctx.clearRect(0, 0,canvas.width(), canvas.height());
        $.each(rectList, function(n,v){
            drawRect(rectList[n][0],rectList[n][1],rectList[n][2],rectList[n][3]);
            ctx.stroke();
        });

另外再写个getCanvasPoint方法,主要把点击坐标换算成Canvas里面的坐标:

        var getCanvasPoint = function(x,y) {
            var canvasOffset = canvas.offset();
            return {
                x: x - canvasOffset.left,
                y: y - canvasOffset.top
            }
        }

是时候该isPointInPath方法登场,实现点中某个矩形的时候改变矩形边框的颜色

        canvas.on("mousedown", function(e){
            moving = true;
            var point = getCanvasPoint(e.pageX, e.pageY);
            $.each(rectList, function(n, v) {
                drawRect(rectList[n][0],rectList[n][1],rectList[n][2],rectList[n][3]);
                if(ctx.isPointInPath(point.x, point.y)){
                    moveItem = n;
                    startPos.x = point.x;
                    startPos.y = point.y;
                    ctx.strokeStyle = "#ff4444";
                } else {
                    ctx.strokeStyle = "#000";
                }
                ctx.stroke();
            })
        });

这里我们从预定义的rectList中依次拿出每个长方形参数,分表是左上角的x坐标,y坐标,长,宽。然后先画出长方形的路径,画完一个用isPointInPath方法判断一下点击点是否在当前路径中,如果在,就给当前的改颜色,如果不在就画出来然后继续画下一个长方形继续判断路径。

是不是很麻烦,isPointInPath只对当前路径有效,也就说我们不能把三个长方形都画完了再判断某个点是否在第一个长方形路径中,而必须是每次画完一个长方形就要判断一次,而且Canvas中路径是不能保存的,要改变某一条路径,就必须把整个Canvas重新画一遍。

所以这里我们在循环中不单单是绘制了点中的长方形,还要绘制没点的长方形。

拖动元素

基本原理就是上面那样,看到这里,你已经洞悉了Canvas实现点击等事件的小秘密。那我们就继续实现拖动其中的元素吧。

        canvas.on("mousemove", function(e){
            if( !moving && moveItem == null ) {
                return;
            }
            ctx.clearRect(0, 0,canvas.width(), canvas.height());
            var point = getCanvasPoint(e.pageX, e.pageY);
            $.each(rectList, function(n, v) {
                var moveX = 0,
                    moveY = 0;

                if(n !== moveItem ){
                    ctx.strokeStyle = "#000";
                } else {
                    moveX = point.x - startPos.x;
                    moveY = point.y - startPos.y;

                    ctx.strokeStyle = "#ff4444";
                }
                drawRect(rectList[n][0] + moveX,rectList[n][1] + moveY,rectList[n][2],rectList[n][3]);
                ctx.stroke();
            })

        });

依然是熟悉的味道,依然是熟悉的配方。我们还是依赖mousemove方法,移动端请用touchmove方法。

因为之前在点击的时候我们已经把是哪个长方形被选中给保存在变量moveItem中了,所以这里就直接判断就好了。是要拖动的元素就把长方形的x,y对应拖动的距离进行更新。同样是要把整个Canvas都绘制一遍。

另外在mouseup事件中保存下状态,方便下次绘画:

        canvas.on("mouseup", function(e){
            moving = false;

            $.each(rectList, function(n){
                if (n == moveItem){
                    var point = getCanvasPoint(e.pageX, e.pageY);

                    rectList[n][0] += point.x - startPos.x;
                    rectList[n][1] += point.y - startPos.y;
                }
            });
            startPos = {}
            moveItem = null
        });

记住:在Canvas中不管你搞什么幺蛾子,都要按照之前的规则把所有路径绘制一遍。

上个效果图:

完整代码:

https://github.com/bob-chen/canvas-demo/blob/master/basic/movebymouse.html

参考

http://jo2.org/html5-canvas-ispointinpath/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Android-薛之涛

Android-ConstraintLayout详解(下)

我们接着上一篇说一说ConstraintLayout的其他属性,如果没看过上一篇:Android-ConstraintLayout详解 先罗列一下此次的会议内...

1963
来自专栏LeoXu的博客

[翻译]Ext JS 教程-布局和容器 原

布局系统是ExtJS中一个强大的组成部分。它控制着应用程序的每一个组件的尺寸和位置。这个操作手册包含开始使用布局的基础操作。

1281
来自专栏iOS开发攻城狮的集散地

iOS 图片浏览的放大缩小

3444
来自专栏进步博客

iOS编程101:如何创建圆形头像和圆角图片

IOS7的一个变化是相对于方形图像,更偏爱于使用圆形图像。在内置的应用中可以看到圆形图标或圆形图像,如联系人和电话应用。这篇短文中,我们将探讨CALayer类,...

1302
来自专栏python学习指南

Python爬虫(二十四)_selenium案例:执行javascript脚本

本章叫介绍如何使用selenium在浏览器中使用js脚本,更多内容请参考:Python学习指南 隐藏百度图片 #-*- coding:utf-8 -*- ...

22510
来自专栏糊一笑

移动端APP列表点透事件处理方法

关于点透事件这里不再赘述,如果不清楚的可以上网搜一搜,或者看小火柴的这篇文章。 这里是自己在做移动端时,在列表滑动的时候,遇到的点透问题。出现这个问题的来由是因...

3745
来自专栏Android机动车

Material Design整理(四)——DrawerLayout

注意:在侧滑菜单区必须设置 android:layout_gravity 这个属性,只要布局中设置了android:layout_gravity,它就是侧滑菜单...

1271
来自专栏Golang语言社区

golang把文件复制到另一个目录

//本程序 主要功能是把A文件夹下的文件与B目录下文件对比,如果找到就覆盖到B相应的目录下。 // 用法: merge A目录 B目录 // merge....

37012
来自专栏xx_Cc的学习总结专栏

六天完成一个简单iOS App - 第二天

2935
来自专栏向治洪

React Native导航器之react-navigation使用

在上一节Navigation组件,我们使用系统提供的导航组件做了一个跳转的例子,不过其实战能力不强,这里推荐一个超牛逼的第三方库:react-navigatio...

2K7

扫码关注云+社区

领取腾讯云代金券