原生JS实现图片裁剪功能

news2024/12/25 10:05:28

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

<!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.coloradmin.cn/o/683012.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

物种气候生态位动态量化与分布特征模拟----R语言

在全球气候快速变化的背景下&#xff0c;理解并预测生物种群如何应对气候变化&#xff0c;特别是它们的地理分布如何变化&#xff0c;已经变得至关重要。利用R语言进行物种气候生态位动态量化与分布特征模拟&#xff0c;不仅可以量化描述物种对环境的需求和适应性&#xff0c;预…

【断路器型号字母含义解析】- 米思米机械设备知识分享

断路器型号意义含义 目前我国断路器型号根据国家技术标准的规定&#xff0c;一般由文字符号和数字按以下方式组成。其代表意义为&#xff1a; ①;产品字母代号&#xff0c;用下列字母表示&#xff1a; S;少油断路器&#xff1b; D;多油断路器&#xff1b; K;空气断路器&#xf…

听说你还不知道什么是 python?带你深入理解什么是 python

文章目录 前言什么是pythonpython的由来我们为什么要学习python帮助python学习的网站 前言 各位朋友们&#xff0c;大家好。在之后的时间里&#xff0c;我将陆续为大家分享我在python学习过程中学习到的知识点&#xff0c;如果你也对python感兴趣的话&#xff0c;欢迎大家来订…

持 PMP®证书增持 CSPM-2证书,先下手就对了!

2023年6月起&#xff0c;持有PMP证书的朋友可以直接增持一个同等级证书CSPM-2&#xff0c;不用重新考试&#xff0c;不用重新学习&#xff0c;原PMP证书不影响正常使用&#xff0c;相当于多了一个国标项目管理领域的证书。 第一步准备资料 1、填写能力评价表(简历和业绩不用填…

Stateflow状态图

目录 1.Stateflow简介 2.Stateflow编辑 2.1 打开方式 2.2 状态 2.3 状态名称 2.4 迁移 2.4.1 迁移 2.4.2 默认迁移 2.4.3 迁移有效条件 3 数据与事件 3.1 数据 3.2 事件 4 示例 1.Stateflow简介 Stateflow对象可分为图形对象和非图形对象。 图形对象包括&#xff1…

LangChain 介绍及相关组件使用总结

一、langChain LangChain 是一个由语言模型LLMs驱动的应用程序框架&#xff0c;它允许用户围绕大型语言模型快速构建应用程序和管道。 可以直接与 OpenAI 的 ChatGPT 模型以及 Hugging Face 集成。通过 langChain 可快速构建聊天机器人、生成式问答(GQA)、本文摘要等应用场景。…

Nginx做图片服务器

前言&#xff1a; Nginx是一个高性能的HTTP和反向代理web服务器,以及负载均衡器。根据nginx是高性能的http服务器&#xff0c;因此可以用作图片服务器使用。 本案例是在docker安装nginx来操作的。 Nginx安装 1、首先是docker的安装 详情参考&#xff1a;docker安装 2、拉取ng…

倒计时2天,解锁亚马逊云科技中国峰会 Dev Lounge 玩法

2023 亚马逊云科技中国峰会即将重磅登陆上海 主会场与分论坛云集百余位重磅嘉宾 开发者会客厅也已集齐全球优秀开源社区 倒计时2天&#xff0c;赶快报名参与这场顶级盛会&#xff01; 开发者大讲堂&#xff0c;圆桌讨论给你精彩 数据对于企业和个人来说都至关重要&#xff0…

【kubernetes系列】Kubernetes中的重要概念

​ 在学习k8s之前&#xff0c;必须先了解 Kubernetes 的几个重要概念&#xff0c;它们是组成 Kubernetes 集群的基石。&#xff08;参考Kubernetes权威指南&#xff09; 一、Master Kubernetes 里的Master指的是集群的控制节点&#xff0c; 每个Kubernetes 集群里至少需要有一…

大数据应用——hive实验

任务一&#xff1a;完成Hive内嵌模式部署 1.1 Hive部署 官网下载Hive安装包 &#xff08;1&#xff09;官网地址&#xff1a;Apache Hive &#xff08;2&#xff09;文档查看地址&#xff1a; https://cwili.apache.org/confluence/display/Hive/GettingStarted &#xff08;2&…

年轻人存款难吗?探讨现实挑战与如何应对

年轻人存款难吗&#xff1f;探讨现实挑战与如何应对 近日&#xff0c;一项调查显示“大概五分之一的年轻人存款在一万元以内。10万元存款是一个“坎”&#xff0c;存款超过10万就会超过53.7%的人。”这个数据引发了关于“年轻人”和“存款”的广泛关注和讨论。作为社会的未来&…

使用 Maya、ZBrush 和 XGen 创建极其逼真的美女肖像

今天云渲染小编给大家带来的是CG艺术家Hadi Karimi 的朋友 Clara 肖像模型背后的工作流程&#xff0c;展示了头发是如何使用 XGen 完成的&#xff0c;并解释了为什么历史人物很难进行逼真的渲染。 介绍 大家伙&#xff0c;我是 Hadi Karimi&#xff0c;是一名来自伊朗的自由 C…

.net点选验证码实现思路分享

哈哈好久没冒泡了&#xff0c;最进看见点选验证码有点意思&#xff0c;所以想自己写一个。 先上效果图 如果你被这个效果吸引了就请继续看下去。 贴代码前先说点思路&#xff1a; 1.要有一个汉字库,并按字形分类。&#xff08;我在数据库里是安部首分类的&#xff09; 2.获…

力扣题库刷题笔记13--罗马数字转整数

1、题目如下&#xff1a; 2、个人Python代码实现&#xff1a; 虽然前面刚刷了整数转罗马数字&#xff0c;而且本题难度为简单&#xff0c;实际上做出来还是花了很多时间&#xff0c;不亚于前面整数转罗马数字。 以上的思路&#xff0c;主要是声明一个列表temp&#xff0c;然后遍…

【MYSQL】—— MySQL 在 Centos 7环境安装

本期&#xff0c;我将给大家介绍的是如何在【Linux】上安装相应的 mysql。 目录 1. 卸载不要的环境 2. 检查系统安装包 3. 获取mysql官⽅yum源 4. 安装mysql yum 源&#xff0c;对⽐前后yum源 5. 看看能不能正常⼯作 6. 安装mysql服务 7. 启动服务 8. 查看启动服务 9.…

从“特种兵旅游”到“citywalk”,渡远户外还能热下去么?

过去风靡一时的“户外露营热”&#xff0c;有些企业赶上了&#xff0c;有些没有。去年坐上“末班车”的渡远户外也想尝些甜头。 套上“户外”概念的渡远户外&#xff0c;或许正在被质疑“血统”并不“纯正”&#xff0c;主营房车游艇配套产品和水上休闲运动产品&#xff0c;说…

【新星计划Linux】——学习工具VMware介绍

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 前言 本章将会讲解Linux学习工具VMware介绍。 一.VMware介绍 1.前期准备 官方网站&#…

【资料分享】全志科技T507-H评估板规格书(4核ARM Cortex-A53,主频1.416GHz)

1 评估板简介 创龙科技TLT507-EVM是一款基于全志科技T507-H处理器设计的4核ARM Cortex-A53国产工业评估板&#xff0c;主频高达1.416GHz&#xff0c;由核心板和评估底板组成。核心板CPU、ROM、RAM、电源、晶振等所有器件均采用国产工业级方案&#xff0c;国产化率100%。同时&a…

实战:win10安装docker并用docker-compose构建运行容器

文章目录 前言Docker DesktopHyper-V安装 Docker Desktop for Windows下载docker desktopDocker安装目录软连接运行Docker Desktop安装文件Docker Desktop验证 docker-compose构建运行管理容器创建文件目录及配置文件构建并启动容器查看启动的容器并验证 写在最后 前言 Docker…

数字IC前端学习笔记:仲裁轮询(四)

相关文章 数字IC前端学习笔记&#xff1a;LSFR&#xff08;线性反馈移位寄存器&#xff09; 数字IC前端学习笔记&#xff1a;跨时钟域信号同步 数字IC前端学习笔记&#xff1a;信号同步和边沿检测 数字IC前端学习笔记&#xff1a;锁存器Latch的综合 数字IC前端学习笔记&am…