ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

Web前端 Canvas图片加载、拖拽、缩放、绘制多边形、打点,坐标偏移获取和颜色获取 例程

2021-12-17 18:02:22  阅读:264  来源: 互联网

标签:canvas val 缩放 例程 pos 获取 xy var event


前言

参考文章:
https://www.cnblogs.com/HPhone/p/3459957.html
https://blog.csdn.net/weixin_29065959/article/details/112268524
https://blog.csdn.net/poqe131/article/details/51012454
结合实际案例 色度图 进行功能拓展和运用。

功能说明

加载img下的色度图片,显示在canvas中,可以进行图片的拖拽,移动,缩放。
可以根据下放的输入框进行四边形顶点的确定,程序会自动进行连线。
鼠标点击图片会获取点击位置的颜色显示在右侧,并在下发显示坐标位置,和判断此点是否在四边形范围内。

工程下载

码云 GitHub

在线访问

码云page

效果图

在这里插入图片描述

核心源码

index.html

<!DOCTYPE html>
<html>
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <title>
        XYZ色度图
    </title>
    <link rel="shortcut icon" href="img/favicon.ico" />
    <link rel="stylesheet" href="css/index.css">
	<script src="js/jquery-3.4.1.min.js"></script>
</head>
<body>
    <div id="title_div">
        <span>XYZ色度图</span>
    </div>
    <div id="canvas_div">
        <canvas id="canvas" width="1080" height="550">
        </canvas>
        <canvas id="canvas2" width="200" height="200">
        </canvas>
    </div>
    <div id="boundary_div">
        <span>边界点C(x,y):</span>
        <input class="input_common" id="C_x" type="text" value="0.0796">
        <input class="input_common" id="C_y" type="text" value="0.5129">
        <span>边界点M(x,y):</span>
        <input class="input_common" id="M_x" type="text" value="0.4026">
        <input class="input_common" id="M_y" type="text" value="0.4907">
        <span>边界点R(x,y):</span>
        <input class="input_common" id="R_x" type="text" value="0.6985">
        <input class="input_common" id="R_y" type="text" value="0.3013">
        <span>边界点RB(x,y):</span>
        <input class="input_common" id="RB_x" type="text" value="0.1568">
        <input class="input_common" id="RB_y" type="text" value="0.0291">
        <button class="button_common" type="button" onclick="set_boundary()">设置边界范围</button>
    </div>
    <div id="info_div">
        <span>x:</span>
        <input class="input_common" id="x_input" type="text" value="">
        <span>y:</span>
        <input class="input_common" id="y_input" type="text" value="">
        <button class="button_common" type="button" onclick="choose_xy_color()">选中此坐标颜色</button>
        <span id="is_inside_msg">选中坐标是否在范围内:</span>
        <span id="is_inside_ret">--</span>
    </div>
    <div id="bottom">
		<div id="copyright">
			<span>Copyright © </span>
			<span id="copyright_year">--</span>
			<span id="official" title="点击打开www.xxx.com"
				onclick="jump_to_official()">xxx</span>
			<span>, All Rights Reserved.</span>
		</div>
	</div>
</body>
<script src="js/index.js"></script>
</html>

index.js

/*
	参考文章:
	https://www.cnblogs.com/HPhone/p/3459957.html
	https://blog.csdn.net/weixin_29065959/article/details/112268524
	https://blog.csdn.net/poqe131/article/details/51012454
*/
var canvas, context;
var img, //图片对象
    imgIsLoaded, //图片是否加载完成;
    imgX = 0,
    imgY = 0,
    imgScale = 1;

var image_widht = 0;
var image_height = 0;

// 边界ABC的xy值
var arr_xy = [{x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}, {x: 0, y: 0}];

(function init() {
    canvas = document.getElementById('canvas');
    context = canvas.getContext('2d');
    load_img();
})();

// 加载图片
function load_img() {
    img = new Image();
    img.src = "img/1.jpeg";
    // img.crossOrigin = 'Anonymous';
    img.onload = function() {
        imgIsLoaded = true;
        drawImage();
        image_width = img.width;
        image_height = img.height;
    }
}

// 绘制图片
function drawImage() {
    context.clearRect(0, 0, canvas.width, canvas.height);
    context.drawImage(img, 0, 0, img.width, img.height, imgX, imgY, img.width * imgScale, img.height * imgScale);
    set_boundary();
}

// 监听canvas拖动事件
canvas.ontouchstart = function(event) {
	var touch = event.touches[0];
    var pos = window_to_canvas(canvas, touch.clientX, touch.clientY);
    canvas.ontouchmove = function(event) {
	    var touch = event.touches[0];
        canvas.style.cursor = "move";
        var pos1 = window_to_canvas(canvas, touch.clientX, touch.clientY);
        var x = pos1.x - pos.x;
        var y = pos1.y - pos.y;
        pos = pos1;
        imgX += x;
        imgY += y;
        drawImage();
    }

    canvas.ontouchend = function() {
        canvas.ontouchmove = null;
        canvas.ontouchend = null;
        canvas.style.cursor = "default";
    }
    //->console.log("canvas开始坐标 x="+imgX +" & y="+imgY);
}

// 监听canvas放大缩小事件
canvas.onmousewheel = canvas.onwheel = function(event) {
    var pos = window_to_canvas(canvas, event.clientX, event.clientY);
    event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltaY * (-40));
    if (event.wheelDelta > 0) {
        imgScale *= 2;
        imgX = imgX * 2 - pos.x;
        imgY = imgY * 2 - pos.y;
    } else {
        imgScale /= 2;
        imgX = imgX * 0.5 + pos.x * 0.5;
        imgY = imgY * 0.5 + pos.y * 0.5;
    }
    drawImage();
    //->console.log("canvas开始坐标 x="+imgX +" & y="+imgY);
}

// 监听canvas拖动事件
canvas.onmousedown = function(event) {
    var pos = window_to_canvas(canvas, event.clientX, event.clientY);
    canvas.onmousemove = function(event) {
        canvas.style.cursor = "move";
        var pos1 = window_to_canvas(canvas, event.clientX, event.clientY);
        var x = pos1.x - pos.x;
        var y = pos1.y - pos.y;
        pos = pos1;
        imgX += x;
        imgY += y;
        drawImage();
    }

    canvas.onmouseup = function() {
        canvas.onmousemove = null;
        canvas.onmouseup = null;
        canvas.style.cursor = "default";
    }
    //->console.log("canvas开始坐标 x="+imgX +" & y="+imgY);
}

// 监听canvas放大缩小事件
canvas.onmousewheel = canvas.onwheel = function(event) {
    var pos = window_to_canvas(canvas, event.clientX, event.clientY);
    event.wheelDelta = event.wheelDelta ? event.wheelDelta : (event.deltaY * (-40));
    if (event.wheelDelta > 0) {
        imgScale *= 2;
        imgX = imgX * 2 - pos.x;
        imgY = imgY * 2 - pos.y;
    } else {
        imgScale /= 2;
        imgX = imgX * 0.5 + pos.x * 0.5;
        imgY = imgY * 0.5 + pos.y * 0.5;
    }
    drawImage();
    //->console.log("canvas开始坐标 x="+imgX +" & y="+imgY);
}

// 监听canvas点击事件
canvas.onclick = function(event) {
    // 重新绘制
    drawImage();
    var pos = window_to_canvas(canvas, event.clientX, event.clientY);
    //实际点的坐标
    var isX = 0;
    var isY = 0;
    //实际选中坐标 肯定 = 点击的坐标 + canvas的坐标(必须为-数)
    var temp_canvas_x = imgX - imgX * 2;
    var temp_canvas_y = imgY - imgY * 2;

    //console.log("imgX = "+imgX + " & imgY = "+imgY);
    //console.log("temp_canvas_x = "+temp_canvas_x + " & temp_canvas_y = "+temp_canvas_y);

    if (imgScale == 1) { //原始尺寸
        isX = (pos.x + temp_canvas_x);
        isY = (pos.y + temp_canvas_y);
    } else {
        if (imgScale > 1) { //被放大了,实际坐标需要/
            isX = (pos.x + temp_canvas_x) / imgScale;
            isY = (pos.y + temp_canvas_y) / imgScale;
        } else { //被缩小了,坐标点放大
            isX = (pos.x + temp_canvas_x) * (1 / imgScale);
            isY = (pos.y + temp_canvas_y) * (1 / imgScale);
        }
    }

    //处理左上的是否超出图片范围
    if (isX < 0 || isY < 0) {
        console.log("请选择图片区域");
        //return;
    }
    //处理右下区域,主要处理图片是否超出
    if (isX > image_width || isY > image_height) {
        console.log("超出图片尺度");
    }
    console.log("点击的坐标点: x = "+isX + " & y = "+isY);
    console.log("图片完整的宽度 = "+image_width + " & 高度 = "+image_height);
    console.log("缩放级别: "+imgScale);

    var ctx = document.getElementById("canvas").getContext("2d");

    // 开始新路径
    ctx.beginPath();
    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    // 画空心圆 arc(x,y,r,sAngle,eAngle,counterclockwise)
    ctx.arc(pos.x, pos.y, 3, 0, 360, false);
    ctx.stroke();
    ctx.closePath();

    // 校验边界
    check_boundary(isX, isY);

    document.getElementById("x_input").value = pixel_point_to_map_point("x", isX, true);
    document.getElementById("y_input").value = pixel_point_to_map_point("y", isY, true);
    
    // 获取img颜色
    var imgData = ctx.getImageData(pos.x, pos.y, 1, 1);
    red = imgData.data[0];
    green = imgData.data[1];
    blue = imgData.data[2];
    alpha = imgData.data[3];

    console.log("red=" +red+ " green=" +green+ " blue=" +blue+ " alpha=" +alpha);
    document.getElementById("canvas2").style.background = 'rgba('+red+','+green+','+blue+','+alpha+')';
}

//获取窗口中canvas的区域
function window_to_canvas(canvas, x, y) {
    var bbox = canvas.getBoundingClientRect();
    return {
        x: x- bbox.left-(bbox.width- canvas.width) / 2,
        y: y- bbox.top-(bbox.height- canvas.height) / 2 
    };
}

// 设置边界范围
function set_boundary() {
    var inputs = document.getElementById("boundary_div").getElementsByTagName('input');
    for(var i = 0; i < arr_xy.length; i++)
    {
        arr_xy[i].x = pixel_point_to_map_point("x", inputs[2*i].value, false);
        arr_xy[i].y = pixel_point_to_map_point("y", inputs[2*i+1].value, false);
    }

    var ctx = document.getElementById('canvas').getContext('2d');
    //设置颜色
    ctx.fillStyle = "white";

    // 获取相对坐标
    function get_opp_xy(type, val) {
        var temp_canvas_x = imgX - imgX * 2;
        var temp_canvas_y = imgY - imgY * 2;

        if(type == "x")
        {
            return (val * imgScale - temp_canvas_x);
        }
        return (val * imgScale - temp_canvas_y);
    }

    // 获取偏移值
    function get_offset_val(type, index, val) {
        if(type == "x")
        {
            if(index == 0 || index == 2)
                return -val;
            else
                return val;
        }
        else
        {
            if(index == 0 || index == 1)
                return -val;
            else
                return val;
        }
    }

    function offset_val(val) {
        if(imgScale > 1)
            return val;
        return imgScale * val;
    }

    for(var i = 0; i < arr_xy.length; i++)
    {
        // 绘制点 移动4
        ctx.fillRect(get_opp_xy("x", arr_xy[i].x) + get_offset_val("x", i, offset_val(4)), 
            get_opp_xy("y", arr_xy[i].y) + get_offset_val("y", i, offset_val(4)), offset_val(4), offset_val(4));
    }

    // 根据点连线
    // 防止重绘
    ctx.beginPath();
    ctx.lineWidth = 2;
    ctx.strokeStyle = "black";
    for(var i = 0; i < arr_xy.length; i++)
    {
        for(var j = i + 1; j < arr_xy.length; j++)
        {
            // 绘制相邻2点
            if(2 == Math.abs(i - j)) continue;
            ctx.moveTo(get_opp_xy("x", arr_xy[i].x) + get_offset_val("x", i, offset_val(4)) + offset_val(2), 
                get_opp_xy("y", arr_xy[i].y) + get_offset_val("y", i, offset_val(4)) + offset_val(2));
            ctx.lineTo(get_opp_xy("x", arr_xy[j].x) + get_offset_val("x", j, offset_val(4)) + offset_val(2), 
                get_opp_xy("y", arr_xy[j].y) + get_offset_val("y", j, offset_val(4)) + offset_val(2));
        }  
    }
    ctx.stroke();
}

// 检查边界值
function check_boundary(pt_x, pt_y) {
    var pt = {x: pt_x, y:pt_y};
    var pts = [{x: arr_xy[0].x, y: arr_xy[0].y}, {x: arr_xy[1].x, y: arr_xy[1].y}, 
        {x: arr_xy[2].x, y: arr_xy[2].y}, {x: arr_xy[3].x, y: arr_xy[3].y}];

    //console.log("pt.x:"+pt.x+" pt.y:"+pt.y);

    if(checkPP(pt, pts))
    {
        document.getElementById("is_inside_ret").innerText = "是";
        console.log("在指定范围内");
        return true;
    }

    document.getElementById("is_inside_ret").innerText = "否";
    console.log("不在指定范围内");
    return false;
}

// 判断点是否在多边形内 
function checkPP(point, polygon) {
    //计算向量叉乘
    var crossMul = function(v1, v2) {
        return v1.x * v2.y - v1.y * v2.x;
    };

    // 判断两条线段是否相交 
    var checkCross = function(p1, p2, p3, p4) {
        var v1 = {
                x: p1.x - p3.x,
                y: p1.y - p3.y
            },
            v2 = {
                x: p2.x - p3.x,
                y: p2.y - p3.y
            },
            v3 = {
                x: p4.x - p3.x,
                y: p4.y - p3.y
            },
            v = crossMul(v1, v3) * crossMul(v2, v3);
        v1 = {
            x: p3.x - p1.x,
            y: p3.y - p1.y
        };
        v2 = {
            x: p4.x - p1.x,
            y: p4.y - p1.y
        };
        v3 = {
            x: p2.x - p1.x,
            y: p2.y - p1.y
        };
        return (v <= 0 && crossMul(v1, v3) * crossMul(v2, v3) <= 0) ? true : false;
    };

    var p1, p2, p3, p4;
    p1 = point;
    p2 = {
        x: -100,
        y: point.y
    };
    var count = 0;
    //对每条边都和射线作对比 
    for (var i = 0; i < polygon.length - 1; i++) {
        p3 = polygon[i];
        p4 = polygon[i + 1];
        if (checkCross(p1, p2, p3, p4) == true) {
            count++;
        }
    }
    p3 = polygon[polygon.length - 1];
    p4 = polygon[0];
    if (checkCross(p1, p2, p3, p4) == true) {
        count++;
    }

    return (count % 2 == 0) ? false : true;
}

// 选中鼠标坐标的颜色
function choose_xy_color() {
    console.log("choose_xy_color()......");

    var x_input = parseFloat(document.getElementById("x_input").value);
    var y_input = parseFloat(document.getElementById("y_input").value);

    var x = pixel_point_to_map_point("x", x_input, false);
    var y = pixel_point_to_map_point("y", y_input, false);

    // 重新绘制
    drawImage();

    // 校验边界
    check_boundary(x, y);

    var pos_x, pos_y;

    var temp_canvas_x = imgX - imgX * 2;
    var temp_canvas_y = imgY - imgY * 2;

    //console.log("imgX = "+imgX + " & imgY = "+imgY);
    //console.log("temp_canvas_x = "+temp_canvas_x + " & temp_canvas_y = "+temp_canvas_y);

    if (imgScale == 1) { //原始尺寸
        pos_x = x - temp_canvas_x;
        pos_y = y - temp_canvas_y;
    } else {
        if (imgScale > 1) { //被放大了,实际坐标需要/
            pos_x = x * imgScale - temp_canvas_x;
            pos_y = y * imgScale - temp_canvas_y;
        } else { //被缩小了,坐标点放大
            pos_x = x / (1 / imgScale) - temp_canvas_x;
            pos_y = y / (1 / imgScale) - temp_canvas_y;
        }
    }

    var ctx = document.getElementById("canvas").getContext("2d");

    // 开始新路径
    ctx.beginPath();
    ctx.lineWidth = 3;
    ctx.strokeStyle = "black";
    // 画空心圆 arc(x,y,r,sAngle,eAngle,counterclockwise)
    ctx.arc(pos_x, pos_y, 3, 0, 360, false);
    ctx.stroke();
    ctx.closePath();

    var imgData = ctx.getImageData(pos_x, pos_y, 1, 1);
    red = imgData.data[0];
    green = imgData.data[1];
    blue = imgData.data[2];
    alpha = imgData.data[3];

    console.log("red=" +red+ " green=" +green+ " blue=" +blue+ " alpha=" +alpha);
    document.getElementById("canvas2").style.background = 'rgba('+red+','+green+','+blue+','+alpha+')';
}

// 像素点坐标转对应图片上点坐标 (136,961)=>(0,0) (238,961)=>(0.1,0) (136,859)=>(0,0.1)
function pixel_point_to_map_point(type, val, LTR) {
    function getFloat(number, n) {
        n = n ? parseInt(n) : 0;
        if(n <= 0) {
            return Math.round(number);
        }
        number = Math.round(number * Math.pow(10, n)) / Math.pow(10, n); //四舍五入
        number = Number(number).toFixed(n); //补足位数
        return number;
    };

    if(type == "x")
    {
        if(LTR == true)
            return getFloat((val - 136) / 1020, 4);
        else
            return getFloat(val * 1020 + 136, 4); 
    }
    else if(type == "y")
    {
        if(LTR == true)
            return getFloat((val - 961) / -1020, 4);
        else
            return getFloat(val * -1020 + 961, 4); 
    }
    else
    {
        console.log("pixel_point_to_map_point() type error!");
        return val;
    }
}

// 跳转至官网
function jump_to_official() {
    console.log("");
}

报错

控制台报错:Uncaught DOMException: Failed to execute 'getImageData' on 'CanvasRenderingContext2D': The canvas has been tainted by cross-origin data.
无法获取鼠标点击位置颜色。网上说是图片跨域问题,设置img.crossOrigin=‘Anonymous’;可以解决,实测又有新的问题。所以还是将工程部署在web server上,比较方便。我这采用 PHP Study简单部了下。
在这里插入图片描述

标签:canvas,val,缩放,例程,pos,获取,xy,var,event
来源: https://blog.csdn.net/Ikaros_521/article/details/121950995

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有