原生JS实现图片裁剪功能

功能介绍:图片通过原生input上传,使用canvas进行图片裁剪。 裁剪框限制不允许超出图片范围,图片限制了最大宽高(自行修改要的尺寸),点击确认获取新的base64图片数据

注:fixed布局不适用该方案,若是fixed布局请查看另一篇文章
效果图:
在这里插入图片描述
上代码

<!DOCTYPE HTML>
<html>

<head lang="en">
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  <title>图片裁剪</title>
</head>

<body>
  <input id="npt" type="file">
  <div id="box">
    <img style="position:absolute;top:0px;left:0px;opacity: 0.3;" src="" id="img1" />
    <img style="position:absolute;top:0px;left:0px;clip: rect(50px, 250px, 250px, 50px);" src="" id="img2" />
    <!--第三层需用绝对定位浮在上面-->
    <div id="dragDiv" style="position: absolute;width: 200px;height: 200px;border: 1px solid #fff;top:50px;left:50px;">
      <div class="Divmin up-left"></div>
      <div class="Divmin up"></div>
      <div class="Divmin up-right"></div>
      <div class="Divmin right"></div>
      <div class="Divmin right-down"></div>
      <div class="Divmin down"></div>
      <div class="Divmin left-down"></div>
      <div class="Divmin left"></div>
      <div class="Divmin-btn" style="right: 68px;background-color: #2d87f5;" id="confirmBtn">确定</div>
      <div class="Divmin-btn" style="right: 0px;background-color: #f5a52d;">取消</div>
    </div>
    <div style="position: absolute; right: 0;">
      <img src="" id="later" alt="">
    </div>

  </div>

</body>

</html>

<style>
  body {}

  #box {
    width: 1200px;
    height: 600px;
    background: #333;
    position: absolute;
    top: 50px;
    left: 50px;
  }

  .Divmin-btn {
    bottom: -40px;
    width: 60px;
    height: 30px;
    line-height: 30px;
    color: white;
    font-size: 12px;
    text-align: center;
    display: inline-block;
    position: absolute;
    border-radius: 3px 3px 3px 3px;
  }

  .Divmin-btn:hover {
    background-color: rgba(60, 103, 222, 0.6);
    color: #efeeee;
  }

  .Divmin-btn:active {
    background-color: rgba(69, 94, 167, 0.6);
    color: #efeeee;
  }

  .Divmin {
    position: absolute;
    width: 8px;
    height: 8px;
    background: #fff;
  }

  .up-left {
    margin-top: -4px;
    margin-left: -4px;
    cursor: nw-resize;
  }

  .up {
    left: 50%;
    /*父元素盒子dragDiv宽度的一半,注意要有绝对定位*/
    margin-left: -4px;
    top: -4px;
    cursor: n-resize;
  }

  .up-right {
    top: -4px;
    right: -4px;
    cursor: ne-resize;
  }

  .right {
    top: 50%;
    margin-top: -4px;
    right: -4px;
    cursor: e-resize;
  }

  .right-down {
    right: -4px;
    bottom: -4px;
    cursor: se-resize;
  }

  .down {
    bottom: -4px;
    left: 50%;
    margin-left: -4px;
    cursor: s-resize;
  }

  .left-down {
    left: -4px;
    bottom: -4px;
    cursor: sw-resize;
  }

  .left {
    left: -4px;
    top: 50%;
    margin-top: -4px;
    cursor: w-resize;
  }

  #img1,
  #img2 {
    max-width: 600px;
    max-height: 300px;
  }
</style>

<script type="text/javascript">
  //禁止图片被选中
  document.onselectstart = new Function('event.returnValue = false;');
  let confirmBtn = document.getElementById('confirmBtn')
  confirmBtn.addEventListener('click', () => {
    drawRect();
  })


  // 获取图片base64数据
  let npt = document.getElementById("npt");
  npt.onchange = function () {
    let reader = new FileReader();
    reader.readAsDataURL(npt.files[0]);
    reader.onloadend = function (e) {
      img1.src = e.target.result;
      img2.src = e.target.result;
      // console.log(e.target.result);// 图片的base64数据
      getImage(e.target.result)
    };
  }

  let canvas = document.createElement("canvas");
  let ctx = canvas.getContext('2d');

  // 创建图片
  let getImage = function (b64) {
    // 创建图片对象
    let image = new Image();
    image.src = `${b64}`;
    image.onload = function () {
      // 获取原图宽高
      let height = img1.offsetHeight;
      let width = img1.offsetWidth;
      //设置canvas大小与原图宽高一致
      canvas.height = height;
      canvas.width = width;
      // 在canvas绘制图片
      ctx.drawImage(this, 0, 0, width, height);
      // 截图:
      // drawRect();

      // 图片上传后设置裁剪框与图片大小一致
      dragDiv.style.height = img1.offsetHeight + 'px'
      dragDiv.style.width = img1.offsetWidth + 'px'
      dragDiv.style.top = 0 + 'px';
      dragDiv.style.left = 0 + 'px';
      setChoice();
    }
  };

  // 绘制截图矩阵
  let drawRect = function () {
    let top = dragDiv.offsetTop;
    let right = dragDiv.offsetLeft + dragDiv.offsetWidth;
    let bottom = dragDiv.offsetTop + dragDiv.offsetHeight;
    let left = dragDiv.offsetLeft;

    // 截图宽度
    let w = right - left;
    // 截图高度
    let h = bottom - top;
    // 获取截图区域内容,截图区域的像素点矩阵
    let cutImage = ctx.getImageData(left, top, w, h);
    // 裁剪后的base64数据
    let newImage = createNewCanvas(cutImage, w, h);
    later.src = newImage;
    // console.log(newImage);// 裁剪后的base64数据
  };

  var createNewCanvas = function (content, width, height) {
    var nCanvas = document.createElement('canvas');
    var nCtx = nCanvas.getContext('2d');
    nCanvas.width = width;
    nCanvas.height = height;
    nCtx.putImageData(content, 0, 0);// 将画布上指定矩形的像素数据,通过 putImageData() 方法将图像数据放回画布
    return nCanvas.toDataURL('image/png');
  }

  //获取id的函数
  function $(id) {
    if (id.indexOf(".") == 0) {
      let className = id.substring(1, id.length);
      let els = document.getElementsByClassName(className);
      return els[0];
    }
    return document.getElementById(id);
  }

  //获取元素相对于屏幕左边及上边的距离,利用offsetLeft
  function getPosition(el) {
    let left = el.offsetLeft;
    let top = el.offsetTop;
    let parent = el.offsetParent;
    while (parent != null) {
      left += parent.offsetLeft;
      top += parent.offsetTop;
      parent = parent.offsetParent;
    }
    return { "left": left, "top": top };
  }

  let dragDiv = $('dragDiv');
  let box = $('box')
  let img1 = $('img1')
  let rightDiv = $('.right');
  let isDraging = false;
  let contact = "";//表示被按下的触点
  //鼠标按下时
  $('.right').onmousedown = function () {
    isDraging = true;
    contact = "right";
  }
  $('.left').onmousedown = function () {
    isDraging = true;
    contact = "left";
  }
  $('.down').onmousedown = function () {
    isDraging = true;
    contact = "down";
  }
  $('.up').onmousedown = function () {
    isDraging = true;
    contact = "up";
  }
  $('.up-right').onmousedown = function () {
    isDraging = true;
    contact = "up-right";
  }
  $('.right-down').onmousedown = function () {
    isDraging = true;
    contact = "down-right";
  }
  $('.up-left').onmousedown = function () {
    isDraging = true;
    contact = "up-left";
  }
  $('.left-down').onmousedown = function () {
    isDraging = true;
    contact = "down-left";
  }

  //鼠标松开时
  window.onmouseup = function () {
    isDraging = false;
  }

  //鼠标移动时
  window.onmousemove = function (e) {
    var e = e || window.event;
    if (isDraging == true) {
      switch (contact) {
        case "up":
          upMove(e);
          break;
        case "right":
          rightMove(e);
          break;
        case "down":
          downMove(e);
          break;
        case "left":
          leftMove(e);
          break;
        case "up-right":
          upMove(e);
          rightMove(e);
          break;
        case "down-right":
          downMove(e);
          rightMove(e);
          break;
        case "down-left":
          downMove(e);
          leftMove(e);
          break;
        case "up-left":
          upMove(e);
          leftMove(e);
          break;
      }
    }
  }

  //up移动
  function upMove(e) {
    let y = e.clientY;//鼠标位置的纵坐标
    let heightBefore = dragDiv.offsetHeight - 2;//选取框变化前的高度
    let addHeight = getPosition(dragDiv).top - y;//增加的高度
    let height = heightBefore + addHeight
    let top = dragDiv.offsetTop - addHeight
    if (top <= 1 || height <= 1) return
    dragDiv.style.height = height + 'px';//选取框变化后的宽度
    dragDiv.style.top = top + 'px';//相当于变化后左上角的纵坐标,鼠标向上移纵坐标减小,下移增大
    setChoice();
  }

  //right移动
  function rightMove(e) {
    let allWidth = img1.offsetWidth + box.offsetLeft
    let x = e.clientX;//鼠标位置的横坐标
    let widthBefore = dragDiv.offsetWidth - 2;//选取框变化前的宽度
    //let widthBefore = dragDiv.clientWidth;
    if (x >= allWidth) return
    let addWidth = x - getPosition(dragDiv).left - widthBefore;//鼠标移动后选取框增加的宽度
    dragDiv.style.width = widthBefore + addWidth + 'px';//选取框变化后的宽度
    setChoice();
  }

  //down移动
  function downMove(e) {
    let heightBefore = dragDiv.offsetHeight - 2;
    let bottom = box.offsetTop + img1.offsetHeight
    let addHeight = e.clientY - getPosition(dragDiv).top - dragDiv.offsetHeight;
    if (e.clientY >= bottom) return
    let height = heightBefore + addHeight
    dragDiv.style.height = heightBefore + addHeight + 'px';
    setChoice();

  }

  //left移动
  function leftMove(e) {
    let widthBefore = dragDiv.offsetWidth - 2;
    let addWidth = getPosition(dragDiv).left - e.clientX;//增加的宽度等于距离屏幕左边的距离减去鼠标位置横坐标
    let width = widthBefore + addWidth
    let left = dragDiv.offsetLeft - addWidth

    if (left <= 1 || width <= 1) return
    dragDiv.style.width = width + 'px';
    dragDiv.style.left = left + 'px';//左边的距离(相当于左边位置横坐标)等于选取框距父级元素的距离减去增加的宽度
    setChoice();
  }

  //设置选取框图片区域明亮显示
  function setChoice() {
    let top = dragDiv.offsetTop;
    let right = dragDiv.offsetLeft + dragDiv.offsetWidth;
    let bottom = dragDiv.offsetTop + dragDiv.offsetHeight;
    let left = dragDiv.offsetLeft;
    $('img2').style.clip = "rect(" + top + "px," + right + "px," + bottom + "px," + left + "px)";
  }
</script>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/37301.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

在vue中点击弹框给弹框中的表格绑值

场景描述&#xff1a;如下图所示&#xff0c;我们需要点击 ‘账单生成’ 按钮&#xff0c;然后里边要展示一个下图这样的表格。 最主要的是如何展示表格中的内容&#xff0c;一起看看吧&#xff01; <template><!-- 水费 欠费--><el-dialog title"水费欠费…

短视频seo矩阵源码开发与实践分享

在短视频矩阵系统源码开发中&#xff0c;需要注意以下几个细节&#xff1a; 1. 确定系统的功能需求&#xff1a;在开发短视频矩阵系统源码时&#xff0c;必须先明确系统的功能需求&#xff0c;包括用户的基本操作、系统数据的生成和处理等。 2. 定义数据库结构&#xff1a;短…

零售数字化转型如何破局?这篇文章全说清了!

“数字化转型”&#xff0c;一个老生常谈的话题。自19世纪互联网崭露头角&#xff0c;亚马逊和eBay等电商平台崛起&#xff0c;引领电子商务的发展。传统零售业开始意识到在线渠道的重要性&#xff0c;并纷纷推出自己的电子商务网站&#xff0c;从自此进入数字化转型的赛道当中…

利用电价运行策略研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

STM32单片机语音识别台灯控制系统人检测亮度调节

实践制作DIY- GC00156-语音识别台灯控制系统 一、功能说明&#xff1a; 基于STM32单片机设计-语音识别台灯控制系统 二、功能说明&#xff1a; 电路&#xff1a;STM32F103C系列最小系统串口语音识别模块LED灯板1个红外传感器 1.任何时候没有人则关闭灯。有人可以自动打开灯。…

激斗云计算:互联网大厂打响新一轮排位战

大模型如同一辆时代列车&#xff0c;所有科技大厂都想上车。 自去年底ChatGPT一炮而红&#xff0c;国内外数十家科技大厂、创业公司、机构相继下场&#xff0c;一时间掀起大模型的热浪。 《中国人工智能大模型地图研究报告》显示&#xff0c;截至今年5月28日&#xff0c;中国…

contentEditable属性

我们最常用的输入文本内容便是input与textarea&#xff0c;但是有一个属性&#xff0c;可以让我们在很多标签中&#xff0c;如div,table,p,span,body等&#xff0c;可以像input输入框一样&#xff0c;实现文本编辑&#xff0c;这便是contentEditable属性 之前有用到这个属性是在…

快速搭建一个美观且易用的 Django 管理后台 —— django-xadmin

Django-xadmin&#xff08;也称为Xadmin&#xff09;是一个第三方的 Django 应用程序&#xff0c;它提供了一系列工具和模板来快速开发基于 Django 的后台管理界面。使用 Django-xadmin 可以用很少的代码就创建出一个强大的、具备实时查看数据、增、删、改等基本操作的 Django …

LiveNVR监控流媒体Onvif/RTSP功能-安全控制HTTP接口鉴权开启禁止游客访问开启后401 Unauthorized如何播放调用接口

LiveNVR安全控制HTTP接口鉴权开启禁止游客访问开启后401 Unauthorized如何播放调用接口&#xff1f; 1、安全控制1.1、接口鉴权1.2、禁止游客访问 2、401 Unauthorized2.1、携带token调用接口2.1.1、获取鉴权token2.1.2、调用其它接口2.1.2.1、携带 CookieToken2.1.2.2、携带 U…

C人脸识别

1、原始图片&#xff1a; 2、灰度化下&#xff1a; 3、均值滤波&#xff1a; 4、 二值图加边缘检测 5、生成积分图 6、把待检测的人脸区域划分为25个&#xff0c;因为是一个数组&#xff0c;这样分别统计每个区域的像素个数&#xff1a; x0: 60, y0: 100, x1: 157, y1: 200 …

介绍AI绘画课,让智能工具助力创作 释放无限想象力 助你成为绘画大师

演示地址&#xff1a; www.runruncode.com/portal/article/index/id/19458/cid/81.html 画画是一项有趣的活动&#xff0c;它让人充满无限可能。对许多人来说&#xff0c;画画既是一种放松的方式&#xff0c;也是一种与创意、文化和艺术联系的途径。如果你是一个初学者&#x…

云原生——Docker容器化实战

❄️作者介绍&#xff1a;奇妙的大歪❄️ &#x1f380;个人名言&#xff1a;但行前路&#xff0c;不负韶华&#xff01;&#x1f380; &#x1f43d;个人简介&#xff1a;云计算网络运维专业人员&#x1f43d; 前言 "Docker"一词指代了多个概念&#xff0c;包括开源…

uniapp实现聊天消息触,vue3和vue2实现聊天消息触底 scrollTop ,scrollHeight Pc端H5端都适用

uniapp触底SDN链接如下(本人的另一篇博客) uniapp聊天时时触底链接 Pc端 模拟手机端H5 vue3写法 <template><div><!-- 聊天窗体 --><div class"test" id"gundong"><div class"text" v-for"p in chat"&…

2023年上半年总结

2023年上半年总结 引言问答CSDN 竞赛技能树博客原力值粉丝数贡献墙个人能力图新星计划总结 引言 老顾是一个懒癌晚期患者&#xff0c;通常情况下&#xff0c;是一条不折不扣的咸鱼&#xff0c;在工作中&#xff0c;也大多数时间都用来摸鱼了。 摸鱼时间太长&#xff0c;也就有…

RabbitMQ的集群

新建一个虚拟机,重新安装一个RabbitMQ,不会安装的可以看下面的连接: 在Linux中安装RabbitMQ_流殇꧂的博客-CSDN博客 1.修改/etc/hosts映射文件,两台虚拟机都需要修改 vim /etc/hosts 127.0.0.1 node1 localhost.localdomain localhost4 localhost4.localdomain4 ::1 node1 loca…

SpringBoot + Kotlin 中使用 GRPC 进行服务通信

示例项目见&#xff1a;kotlin-grpc 一、导入依赖&#xff1a; import com.google.protobuf.gradle.* plugins { id("org.springframework.boot") version "2.3.1.RELEASE" id("io.spring.dependency-management") version "1.0.9.REL…

idea连接远程MySQL数据库

填写URL&#xff0c;以mysql为例 格式 jdbc:mysql://ip地址:端口号/数据库名 jdbc:mysql://127.0.0.1:3306/ldentification _Information

软件测试基础知识

软件测试的生命周期 软件测试的生命周期和软件的生命周期是不一样的&#xff0c;软件包括需求分析和规划&#xff0c;设计和编码&#xff0c;测试和验证&#xff0c;部署和维护&#xff0c;退役和回收等等&#xff0c;而软件测试的生命周期则是需求分析-测试计划-测试设计&…

对 Jenkins+ANT+Jmeter 接口测试的实践

目录 1、前言 2、框架与数据准备 3、脚本设计 4、整理测试报告 1、前言 JenkinsANTJMeter是一种常见的接口测试实践方案&#xff0c;可以实现自动化的接口测试和持续集成。Jenkins是一个流行的持续集成工具&#xff0c;ANT是一个构建工具&#xff0c;而JMeter是一个功能强大…

maven配置java outofmemory选项

在maven之中选择Add VM options&#xff0c;这样命令就多出来一个关于VM options配置的属性&#xff0c;此时就可以输入对于VM的设置