基于JavaScript实现图片裁剪功能

目录
  • 一、图片文件的上传和读取
  • 二、图片展示和蒙层处理
    • CSS clip-path
  • 三、裁剪框展示
    • 裁剪框的缩放点
    • cursor 鼠标样式
  • 四、裁剪框移动事件
  • 五、裁剪框缩放操作
  • 六、完成裁剪功能
    • drawImage
  • 后记

在前端开发中,当遇到图片或头像上传等功能时,有尺寸分辨率限制的话,就需要用到图片的裁剪功能。想了解图片基础知识的,可见前文图片基础知识介绍。

而canvas的使用,对于我们直接在web端实现图片裁剪功能成为可能。本文将使用前端技术实现一个图片的裁剪功能。

一、图片文件的上传和读取

使用文件上传控件,实现图片上传,获取到图片文件(File对象)后,可以通过 FileReaderURL.createObjectURL 两个API完成对文件数据的转换。前文有描述深入理解前端二进制API知识。

  • FileReader:一般使用readAsDataURL方法将File读取为图片文件的Base64数据,可以直接作为图片数据加载。Base64知识可见前文深入理解Base64字符串编码知识。
  • URL.createObjectURL:则生成一个伪协议的Blob-Url链接,用在这里是一个图片的URL链接,可以加载图片资源。

如下,即响应文件上传事件,以Base64字符串数据加载图片:

// 上传控件事件响应,加载图片文件
document.getElementById('input-file').onchange = (e) => {
  const file = e.target.files[0]
  const reader = new FileReader()
  reader.onload = async (event) => {
    initImageCut(event.target.result)
  }
  reader.readAsDataURL(file)
}

这样读取的数据就是图片Base64字符串数据,可当做图片资源被 Image 对象加载了。

二、图片展示和蒙层处理

获取到图片文件的数据以后,加载图片获取像素宽高:

const img = new Image()
img.src = dataUrl
img.onload = function () {
  resolve(img)
}

一般通过 Image 对象,生成一个img实例,加载图片数据,img实例里包含有图片宽高。

图片的宽高是比重重要的数据,如计算图片展示区的缩放比例,后续裁剪框的拖放和缩放也都需要用到。

如下代码,计算缩放比例(zoom):

zoom = Math.min(WIDTH / img.width, HEIGHT / img.height)
zoom = zoom > 1 ? 1 : zoom

其中,WIDTHHEIGHT 是设定一个固定区域,用来展示图片和裁剪框,值的大小可以随意设置,在显示器可视区域内最好;

zoom的作用,可以方便我们后面获取图片的相对大小。

接下来就可以在页面上展示图片,并设置蒙层处理。

图片的展示,我们这里直接使用html的 <img> 标签:

<img class="image" id="bgMaskImg"/>
<img class="image" id="cutBoxImg"/>

这里使用了两个 <img> 标签元素,两个元素加载同样的图片资源,区别在于:

  • 其中 bgMaskImg 作为底图,设置透明度(如0.5),模拟蒙层效果;
  • cutBoxImg 作为裁剪框区域的图片展示,即非蒙层的清晰图片。

这里图片展示需要达到的效果,如下:

上图的展示中,看上去有蒙层效果的就是第一个img标签;

而中间区域清晰的图片块则是第二个img标签的效果,这里是借助CSS中的 clip-path 属性来完成的。

然后,需要给两个img标签元素加载图片,并设置各元素的样式:

bgMaskImgElm.src = cutBoxImgElm.src = imgUrl

setStyle()

CSS clip-path

clip-path 是一个CSS属性,能够只展示元素的一块部分区域,而其他区域隐藏起来。

这个特性正好可以作为裁剪功能使用,这里使用在图片上,就能模拟出来裁剪蒙层的效果。

CSS之前有个 clip 属性,但是已经废弃,虽然部分浏览器还支持,但建议使用 clip-path

clip-path属性有很多取值,我们使用它的多边形值 polygon,模拟方形的裁剪区域:

img {
  clip-path: polygon(0 0, 100px 0, 100px 100px, 0 100px);
}

如上代码,使用像素值,定位方形的四个顶点的坐标(左上、右上、右下、左下),展示出图片的一个方形裁剪区域,这时候其他区域不可见,就形成了上面图片展示中的清晰区域。

因为这块裁剪区域会随着裁剪框移动或者缩放而进行改变,所以需要通过JS来改变:

const clipPath = `polygon(...)`
cutBoxImgElm.style.clipPath = clipPath

在移动或拖动后,重新计算裁剪区域的坐标点,再进行 clip-path 属性的更新。

三、裁剪框展示

上面实现了图片的加载展示、蒙层和裁剪区域的处理后,接下来,就是对裁剪框的实现。

裁剪框使用div的方式就可以了,定义一个裁剪框:

<div id="cutBox" class="cut-box" style="display: none;">
...
</div>

这里需要注意的是通过JS来改变这个裁剪div的位置和大小,并且也要同步更改上文提到的 clip-path 属性:

cutBoxElm.style.width = cutBoxWidth * zoom  - 2 + 'px'
cutBoxElm.style.height = cutBoxHeight * zoom  - 2 + 'px'
cutBoxElm.style.left = cutBoxLeft + 'px'
cutBoxElm.style.top = cutBoxTop + 'px'

裁剪框的缩放点

裁剪框的展示,一般会设计八个缩放点,如下图所示:

需要注意,图上标注了缩放点大致的名称,下文会涉及到对应的点的事件处理,可以清楚是哪个操作。

这样的代码用div也较好实现,使用小图标或者CSS画出粗线即可,放入 cutBox 的裁剪框div下,跟随移动。

<div class="box-corner topleft" style="cursor:nw-resize;"></div>
<div class="box-corner topright" style="cursor:ne-resize;"></div>
<div class="box-corner bottomright" style="cursor:se-resize;"></div>
<div class="box-corner bottomleft" style="cursor:sw-resize;"></div>
<div class="box-middle topmiddle" style="cursor:n-resize;"></div>
<div class="box-middle bottommiddle" style="cursor:s-resize;"></div>
<div class="box-middle leftmiddle" style="cursor:w-resize;"></div>
<div class="box-middle rightmiddle" style="cursor:e-resize;"></div>

上面html代码就是定义的八个点,配以对应的css样式,就达到所需要的效果了。

需要注意的是,鼠标样式的变更,这里的缩放点,当鼠标hover上去的时候,需要展示不同的鼠标样式。

cursor 鼠标样式

cursor属性主要设置光标的类型,在浏览器上使用鼠标操作时,会显示鼠标的不同样式图标。

pointer 悬浮的手指样式,grab 抓手样式等。

裁剪框里使用的是八个缩放相关的鼠标指针样式:

描述
nw-resize、se-resize 左斜双箭头
ne-resize、sw-resize 右斜双箭头
n-resize、s-resize 垂直双箭头
w-resize、e-resize 水平双箭头

四、裁剪框移动事件

处理好图片、蒙层效果、裁剪框的结构和展示以后,下面就需要增加一些操作事件。

首先要处理的是裁剪框的移动事件,让裁剪框可以在图片区域内任意移动,使用基本的鼠标事件:

isMoveDown = false
cutBoxElm.addEventListener('mousedown', (event) => {
  isMoveDown = true
  const { offsetLeft, offsetTop } = cutBoxElm
  const disX = event.clientX - offsetLeft
  const disY = event.clientY - offsetTop

  document.onmousemove = (docEvent) => {
    const left = docEvent.clientX - disX
    const top = docEvent.clientY - disY
    if (isMoveDown) {
      //...
    }
    docEvent.preventDefault()
  }
  document.onmouseup = () => {
    isMoveDown = false
  }
})

移动裁剪框,不牵涉到缩放,只是对位置信息跟随鼠标同步更新,所以重点是计算鼠标事件和裁剪框的偏移位置数据。

在计算位置定位数据后,还需要做的一件事,是不能让裁剪框脱离图片区域,即不能移动到图片外面去,这样是无效的。

// 裁剪框 left 数据
cutBoxLeft = Math.max(0, Math.min(left, curImageWidth - curCutBoxWidth))
// 裁剪框 top 数据
cutBoxTop = Math.max(0, Math.min(top, curImageHeight - curCutBoxHeight))

以上代码,通过对图片宽高和裁剪框宽高的处理,获取裁剪框位置的限制点。

五、裁剪框缩放操作

裁剪框的缩放事件,也需要进行绑定,本文示例,通过对八个缩放点进行各自的事件绑定,仍然是通过与移动裁剪框一样的鼠标事件:

// 是左上角的缩放点
document.querySelector('.topleft').addEventListener('mousedown', (event) => {
  reSizeDown('topleft', event)
})
// ...
// 其他点各自绑定

reSizeDown 函数中仍然是对 mousemove 事件的处理:

let isResizeDown = false
function reSizeDown (type, event) {
  isResizeDown = true
  document.onmousemove = (docEvent) => {
    const disX = docEvent.clientX - event.clientX
    const disY = docEvent.clientY - event.clientY
    if (isResizeDown) {
      let cutW = currentCutBoxWidth
      let cutH = currentCutBoxHeight
      switch (type) {
        case 'topleft':
          cutBoxLeft = Math.min(currentBoxLeft + (currentCutBoxWidth * zoom ) - 16, Math.max(0, currentBoxLeft + disX))
          cutBoxTop = Math.min(currentBoxTop + (currentCutBoxHeight * zoom ) - 16, Math.max(0, currentBoxTop + disY))

          const nwWidth = currentCutBoxWidth - (disX / zoom)
          const nwHeight = currentCutBoxHeight - (disY / zoom)
          cutW = +(cutBoxLeft > 0 ? nwWidth : (currentCutBoxWidth + currentBoxLeft / zoom)).toFixed(0)
          cutH = +(cutBoxTop > 0 ? nwHeight : (currentCutBoxHeight + currentBoxTop / zoom)).toFixed(0)
          break
        // case 'topright':
        // ...
        // 对每个缩放点进行处理
      }
      // ...
    }
  }
  document.onmouseup = () => {
    isResizeDown = false
  }
}

以上代码,以左上角的缩放为例,拖动左上角缩放时,裁剪框的位置和宽高尺寸都会发生变化,所以需要计算这四个值(left, top, width, height)。

裁剪框四个角的位置都需要类似这样的处理,但是其他四个直线方向上的缩放,则要相对简单一点:

case 'leftmiddle':
  cutBoxLeft = Math.min(currentBoxLeft + (currentCutBoxWidth * zoom ) - 16, Math.max(0, currentBoxLeft + disX))
  const wWidth = currentCutBoxWidth - (disX / zoom)
  cutW = +(cutBoxLeft > 0 ? wWidth : (currentCutBoxWidth + currentBoxLeft / zoom)).toFixed(0)
  break

以上代码,只需要计算 left 偏移和裁剪框宽度即可。

有了位置和宽高数据以后,实时改变裁剪框的样式属性和蒙层图片的 clip-path 属性,同步变化,裁剪框的事件处理就基本完成了。

六、完成裁剪功能

事件绑定后,裁剪框的基本功能就已经完成了,剩下的,就是进行最后的图片裁剪操作。

裁剪框进行移动或者缩放以后,我们需要获取到当前裁剪框的数据:位置和宽高数据。

根据位置和宽高,就可以使用canvas裁剪出图片:

const left = cutBoxLeft / zoom
const top = cutBoxTop / zoom

const myCanvas = document.createElement('canvas')
const ctx = myCanvas.getContext('2d')
myCanvas.width = cutBoxWidth
myCanvas.height = cutBoxHeight

ctx.drawImage(imgObj, left, top, cutBoxWidth, cutBoxHeight, 0, 0, cutBoxWidth, cutBoxHeight)

如上代码,

位置信息之前计算的是相对位置,还原到原始图片上,就需要通过缩放比例进行还原处理;裁剪框宽高因之前已进行还原,这里直接取值即可。

画布 myCanvas 裁剪绘制成功后,就得到了所需要的图像,可以直接将画布展示在页面上,也可以将画布导出为图像Base64数据或者Blob-Url后再加载。

myCanvas.toBlob((blob) => {
  const url = URL.createObjectURL(blob)
}, 'image/jpeg')
// 或
myCanvas.toDataURL()

drawImage

drawImage 是canvas中一个的API,用来处理各种图像操作的,它的语法有三种,我们取可以做裁剪的:

context.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight)

其中,

  • 参数 sx, sy, sWidth, sHeight,就可以理解为对原始图片按该位置和宽高尺寸进行裁剪,就可以得到一张裁好的新图;
  • 参数 dx, dy, dWidth, dHeight,就是将上面裁好的新图,按照该位置和宽高,绘制到canvas画布上,这个时候,就等到了裁剪后新图片在canvas里的展示。

下图就是示例里裁剪功能完整的界面展示效果:

后记

经过以上步骤,一个基于前端技术的基础的图片裁剪功能就完成了。 图片裁剪还可以有多种方法进行处理,示例使用的都是原始标签和API,方便理解。 后续也可以进行各种组件化的封装,不论vue、react、webcomponent等,都可以快速的引入进行封装。

到此这篇关于基于JavaScript实现图片裁剪功能的文章就介绍到这了,更多相关JavaScript图片裁剪内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • js+jquery实现图片裁剪功能

    现在我们在使用各大网站的个人中心时,都有个上传个人头像的功能.用户在上传了个人照片之后,可能不符合网站的要求,于是要求用户对照片进行裁剪,最终根据用户裁剪的尺寸生成头像.这个功能真是太棒了,原来不懂js的时候,感觉很神奇,太神奇了.心想哪天要是自己也能搞明白这里面的技术,那该多牛呀~大家是不是也有何我一样的想法呀~哈哈~~ 下面我们就来用javascript来实现这个功能吧. 复制代码 代码如下: <!DOCTYPE html> <html xmlns="http://www.

  • javascript 图片裁剪技巧解读

    学php gd库 看到有图片裁剪 正好整一个 嗯 其实挺简单的 php版 复制代码 代码如下: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Res

  • 基于原生JS实现图片裁剪

    下面是我自己写的图片裁剪的功能介绍: 可以利用鼠标拖拉,产生裁剪框 可以改变裁剪框大小 点击确定,返回裁剪数据 原理 完成裁剪的方法有两种: 1.利用HTML5新增拖拽事件drag drop等 2.传统方法,利用鼠标事件,mousedown.mousemove等 在这里,我们采用方法2. 裁剪区域的形成 要进行裁剪首先要形成裁剪区域,这个裁剪区域的形成我们可以与鼠标移动的距离相关联.鼠标移动多远,裁剪区域就有多大.如下图: 如上图所示鼠标的横向移动距离与纵向移动距离共同组成了裁剪区域的宽和高.

  • 基于JavaScript实现图片裁剪功能

    目录 一.图片文件的上传和读取 二.图片展示和蒙层处理 CSS clip-path 三.裁剪框展示 裁剪框的缩放点 cursor 鼠标样式 四.裁剪框移动事件 五.裁剪框缩放操作 六.完成裁剪功能 drawImage 后记 在前端开发中,当遇到图片或头像上传等功能时,有尺寸分辨率限制的话,就需要用到图片的裁剪功能.想了解图片基础知识的,可见前文图片基础知识介绍. 而canvas的使用,对于我们直接在web端实现图片裁剪功能成为可能.本文将使用前端技术实现一个图片的裁剪功能. 一.图片文件的上传和

  • 基于RxPaparazzo实现图片裁剪、图片旋转、比例放大缩小功能

    前言:基于RxPaparazzo的图片裁剪,图片旋转.比例放大|缩小. 效果: 开发环境:AndroidStudio2.2.1+gradle-2.14.1 涉及知识: 1.Material Design (CardView+CoordinatorLayout+AppBarLayout+NestedScrollView+CollapsingToolbarLayout+Toolbar+FloatingActionButton)使用 2.butterknife注解式开发 3.基于RxJava+RxAn

  • 基于JavaScript实现图片剪切效果

    学会如何获取鼠标的坐标位置以及监听鼠标的按下.拖动.松开等动作事件,从而实现拖动鼠标来改变图片大小. 还可以学习css中的clip属性. 一.CSS实现图片不透明及裁剪效果. 图片剪切三层结构 1.第一层opacity,给图层设置透明度 2.第二层clip,clip属性:对图片进行裁剪,实现图像的一部分显示,其他部分进行隐藏 3.第三层选取框absolute(与第二层重叠的),包括八个触点的效果 html代码: <div id="box"> <img src=&quo

  • cropper js基于vue的图片裁剪上传功能的实现代码

    前些日子做了一个项目关于vue项目需要头像裁剪上传功能,看了一篇文章,在此基础上做的修改完成了这个功能,与大家分享一下.原文:http://www.jb51.net/article/135719.htm 首先下载引入cropper js, npm install cropper js --save 在需要的页面引入:import Cropper from "cropper js" html的代码如下: <template> <div id="demo&quo

  • 一个基于react的图片裁剪组件示例

    开始 写了一年多vue,感觉碰到了点瓶颈,学习下react找找感觉.刚好最近使用vue写了个基于cropperJS的图片裁剪的组件,便花费了几个晚上的功夫用react再写一遍.代码地址 项目是使用create-react-app来开发的,省去了很多webpack配置的功夫,支持eslint,自动刷新等功能,使用前npm install并npm start即可.推荐同样是新学习react的人也用用看. 项目写的比较简陋,自定义配置比较差,不过也是完成了裁剪图片的基本功能,希望可以帮助到初学reac

  • Vue图片裁剪功能实现代码

    目录 一.效果展示: 1.表单的图片上传项: 2.裁剪框页面 二.代码部分 1.首先安装Vue-Cropper,基于此组件的基础上开发的裁剪页面 2.裁剪弹窗的组件编写: 3.[图片上传表单项]组件编写 一.效果展示: 1.表单的图片上传项: - 新增时默认一个空白Input框 - 更新时展示以往上传存放的图片, - 点击[查看]浏览完整大小 - 点击[删除]清空src地址,重新上传新照片 2.裁剪框页面 - 先选择裁剪的图片 - 右侧展示裁剪区域 - 支持放大缩小,图片旋转 - 点击[上传图片

  • java实现的图片裁剪功能示例

    本文实例讲述了java实现的图片裁剪功能.分享给大家供大家参考,具体如下: PicCut.java: package Tsets; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Iterator; import javax

  • 基于JavaScript实现微信抢红包功能

    金额随机:额度在0.01和(剩余平均值*2)之间. /** * 抢红包 * @param {[number]} totalAmount [总金额] * @param {[number]} totalPeople [总人数] * @return {[Array]} [每个人抢到的金额] */ function assign(totalAmount, totalPeople){ var remainAmount = +totalAmount; var remainPeople = +totalPeo

  • 基于JavaScript实现图片连播和联级菜单实例代码

    <!DOCTYPE html> <html> <head> <title>图片轮播</title> <style> div{ border: 1px solid red; width:218px; height: 218px; } .show{ display: inline-block; } .hide{ display: none; } </style> <meta charset="UTF-8&quo

  • 详解vue项目中实现图片裁剪功能

    演示地址 https://my729.github.io/picture-crop-demo/dist/#/ 前言 vue版本:3.6.3 https://cli.vuejs.org/zh/ cropperjs: 1.5.1 https://github.com/fengyuanchen/cropperjs elementUI https://element.eleme.io/#/zh-CN 使用 cropperjs插件 和 原生canvas 两种方式实现图片裁剪功能 使用cropperjs插件

随机推荐