前端入门(自留)——HTML5 Canvas绘图(实现放大镜)

85 阅读4分钟

相关知识

  • canvas标签的常用属性:hidden是否隐藏、width画布宽度、height画布高度
  • document对象
常用方法含义实例备注
getElementById指定id属性的第一个对象document.getElementById('pic')
getElementByName指定name属性的对象集合document.getElementByName('hobbies')常用于单行文本框、复选框等具有name属性的元素
getElementByTagName指定标签名的元素集合document.getElementByTagName('input')元素在文档中的顺序即为集合中的顺序;当参数为‘*’,返回页面所有标签
getElementByClassName指定class属性的对象集合document.getElementByClassName
  • offset(偏移量)
    • offsetParent —— 获得被定位的最近的祖先元素
    • offsetLeft —— 当前元素相对于 offsetParent节点 左边界的偏移像素值
    • offsetTop —— 当前元素相对于 offsetParent节点 上边界的偏移像素值
    • 当元素的 style.display 设置为 "none" 时,offsetParent 返回 null

页面效果

共三个板块,左侧为显示的图片,右边图片与左侧等大,为左侧图片放大后的效果,下方为五张可选择放大的图片。

鼠标在左侧图片上移动时,会出现黄色遮罩区,遮罩区覆盖的部分,会在右侧放大,点击下方图片可切换被放大的图片。

放大后的清晰度 取决于原图的像素

image.png

magnifier.gif

注意事项

  • canvas画布大小只能通过height、width属性设置,不能在样式中设置height、width
  • canvas裁切的是img.src的原图而非img容器的图像, 需要通过img.naturalHeight和img.naturalHeight获取原图的宽高, 和img容器作比,来获取图像缩放的比例()
    • img.naturalWidth / canvas_width 原图与画布的宽之比
    • img.naturalHeight / canvas_width 原图与画布的高之比
    • 绘图的原图坐标 : shadeX × 宽之比,shadeY × 高之比
    • 绘制的原图大小 : shade_width × 宽之比, shade_height × 高之比
  • 关于shade遮罩区的X坐标如何计算(Y坐标同理)
    • ? = e.pageX - boxX - shade_width/2 image.png

代码

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>放大镜</title>
    <link rel="stylesheet" href="../css/magnifier.css">
    <script type="text/javascript" src="../js/magnifier.js"></script>
</head>

<body>
    <div class="left_pic">
        <div id="box">
            <img id="showPicture" src="../images/magnifier/1.jpg" alt="">
            <!-- 遮罩层 -->
            <div id="shade"></div>
        </div>
    </div>
    <canvas id="mycanvas" width="1000px" height="1000px"></canvas>
    <ul class="small_piclist" id="picsList">
        <li><img src="../images/magnifier/5.jpg" alt="" onclick="changePic(this)"></li>
        <li><img src="../images/magnifier/4.jpg" alt="" onclick="changePic(this)"></li>
        <li><img src="../images/magnifier/3.jpg" alt="" onclick="changePic(this)"></li>
        <li><img src="../images/magnifier/2.jpg" alt="" onclick="changePic(this)"></li>
        <li><img src="../images/magnifier/1.jpg" alt="" onclick="changePic(this)"></li>
    </ul>
</body>

</html>

.left_pic{
    position: relative;
    margin: 50px;
    float: left;
}

/* 图片 */
#box{
    position: relative;
    margin-bottom: 10px;
    width: 1000px;
    height: 1000px;
}

#showPicture{
    width: 100%;
    height: 100%;
    /* object-fit: fill; */
}

/* 遮罩层 */
#shade{
    position: absolute;
    top: 0;
    z-index: 10;
    width: 300px;
    height: 300px;
    /* 设置透明度 */
    /* IE */
    filter: alpha(Opacity = 70);
    /* 火狐 */
    -moz-opacity: 0.7;
    /* chrome */
    opacity: 0.7;
    background-color: #ffc;
    display: block;
}

#picsList{
    clear:both;
    list-style: none;
}

#picsList li{
    height: 400px;
    width: 400px;
    float: left;
    margin-left: 50px;
}

#picsList li img{
    width: 100%;
    height: 100%;
}

#mycanvas{
    margin: 50px;
    float: left;
    display: none;
    background-color: #fff;
    width: 1000px;
    height: 1000px;
}
/*
若修改样式中#box、#shade、#mycanvas的宽高,需要修改以下参数
    // 图片大小(box)
    var pic_width = 1000;
    var pic_height = 1000;

    // 遮罩层大小(shade)
    var shade_width = 300;
    var shade_height = 300;
    
    可优化:动态获取样式或属性的值大小
*/

// 切换展示的图像
function changePic(thumb){
    // 获取缩略图在商品展示区对应的大图
    var showPic = document.getElementById("showPicture");
    showPic.src = thumb.src;

    // 获取缩略图对应的li元素,将边框清除
    var picsList = document.getElementById("picsList");
    var items = picsList.getElementsByTagName("li");
    for (var i=0; i<items.length; i++){
        var thumbImg = items[i].getElementsByTagName("img");
        thumbImg[0].style.border = "";
    }
    // 设置当前选中图片的边框为红色
    thumb.style.border = "4px solid red";
}

// 获取元素顶部与body的距离
function getTop(e){
    var offset = e.offsetTop;
    // offsetParent为离自身最近且经过定位的父元素
    if (e.offsetParent != null)
        offset += getTop(e.offsetParent);
    return offset;
}

// 获取元素左侧与body的距离
function getLeft(e){
    var offset = e.offsetLeft;
    if (e.offsetParent != null)
        offset += getLeft(e.offsetParent);
    return offset;
}

// 图像放大效果
function zoomPicture(){
    var box = document.getElementById("box");
    var showPicture = document.getElementById("showPicture");
    var canvas = document.getElementById("mycanvas");
    var shade = document.getElementById("shade");

    // 整个.left_pic的margin(复合属性无法直接获取即margin,只能获取单一数值)
    var margin = 50;
    
    // 图片大小(box)
    var pic_width = 1000;
    var pic_height = 1000;

    // 遮罩层大小(shade)
    var shade_width = 300;
    var shade_height = 300;

    // 画布大小(mycanvas)
    var canvas_width = canvas.width;
    var canvas_height = canvas.height;

    if (showPicture == null) return false;

    // 绑定鼠标移出图片所触发的事件
    box.onmouseout = function(){
        shade.style.display = "none";
        canvas.style.display = "none";
        document.body.style.cursor = "default";
    };

    // 绑定鼠标移入图片所触发的事件
    box.onmousemove = function(e){
        // 设定鼠标样式
        document.body.style.cursor = "move";
        // 获取图片容器相对于body的距离(可以理解为容器左上角的坐标)
        var boxX = getLeft(box);
        var boxY = getTop(box);

        // 计算遮罩区域左上角的坐标
        var shadeX = e.pageX - boxX - shade_width/2;
        var shadeY = e.pageY - boxY - shade_height/2;

        // 防止遮罩区移动到图片之外
        if (shadeX < 0) shadeX = 0;
        else if (shadeX > pic_width-shade_width) shadeX = pic_width-shade_width;
        if (shadeY < 0) shadeY = 0;
        else if (shadeY > pic_height-shade_height) shadeY = pic_width-shade_height;

        // 使用canvas绘制遮罩区
        var context = canvas.getContext("2d");
        shade.style.display = "block";
        shade.style.left = shadeX + "px";
        shade.style.top = shadeY + "px";
        canvas.style.display = "block";

        // 在右侧绘制放大后的图像
        var img = showPicture;
        var proportion_width = img.naturalWidth / canvas_width;  
        var proportion_height = img.naturalHeight / canvas_width;
        
        // 清除之前画的图像
        context.clearRect(margin, margin, pic_width+margin, pic_height+margin);
        
        // 绘制图像(参数:图片源,绘制的原图坐标,绘制的原图大小,在画布上的坐标,绘制的图片长宽)
        context.drawImage(img, shadeX*proportion_width, shadeY*proportion_height,
                        shade_width*proportion_width, shade_height*proportion_height, 0, 0, canvas_width, canvas_height);
    }
}

//去掉像素单位px
function dealPx(pixelStr){
    var pixel = pixelStr.substring(0,pixelStr.indexOf('px'));
    if (pixel == "") return 0;
    return parseInt(pixel);
}

window.onload = function(){
    //默认第一个图片为选中状态
    var picsList = document.getElementById("picsList");
    var thumb_img = picsList.getElementsByTagName("li")[0].getElementsByTagName("img");
    changePic(thumb_img[0]);
    
    zoomPicture();
}