欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

JavaScript實(shí)現(xiàn)小程序圖片裁剪功能的示例代碼

 更新時(shí)間:2023年03月30日 10:29:39   作者:jimojianghu  
這篇文章主要為大家詳細(xì)介紹了如何利用JavaScript實(shí)現(xiàn)小程序圖片裁剪功能,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起了解一下

在之前的博文中,已經(jīng)介紹了如何使用在前端開(kāi)發(fā)中,實(shí)現(xiàn)較方便自由的圖片裁剪功能,可見(jiàn)博文:如何一步步實(shí)現(xiàn)圖片裁剪功能。

本文將進(jìn)一步講述在微信小程序中,如何實(shí)現(xiàn)圖片的裁剪功能,實(shí)現(xiàn)的效果如下圖所示:

圖片上傳與處理

要做圖片裁剪,首先要解決的是圖片上傳的事情,這塊在微信環(huán)境下,比較好處理,微信小程序提供了多個(gè)文件或圖片的操作接口,讓我們可以很方便的完成這個(gè)步驟。

小程序中圖片上傳比較簡(jiǎn)單,直接使用已提供的接口 chooseMedia ,用于選擇媒體文件(圖片或視頻):

wx.chooseMedia({
  count,
  mediaType: ['image'],
  sizeType: [ 'original' ],
  sourceType: [ 'album' ],
  success: (imgRes) => {
    callBack && callBack(imgRes)
  },
  fail: () => {}
})

除此外,還可以有其他方式,如 wx.chooseMessageFile 接口可以從聊天記錄里選擇圖片,我們可以綜合處理如下:

function selectPhoto (callBack) {
  let itemList = [ '拍照', '從手機(jī)相冊(cè)選擇', '從聊天記錄選擇' ]
  wx.showActionSheet({
    itemList,
    success: (action) => {
      if (action.tapIndex === 0) {
        // 從拍照選擇圖片,使用 chooseMedia
        wx.chooseMedia({ sourceType: [ 'camera' ] })
      } else if (action.tapIndex === 1) {
        // 從手機(jī)相冊(cè)選擇圖片,使用 chooseMedia
        wx.chooseMedia({ sourceType: [ 'album' ] })
      } else if (action.tapIndex === 2) {
        // 從聊天記錄選擇圖片,使用 chooseMessageFile
        wx.chooseMessageFile({})
      }
    }
  })
}

其中,showActionSheet 可以顯示操作菜單,這個(gè)功能也比較常見(jiàn),可以從拍照、相冊(cè)、聊天記錄里選擇文件進(jìn)行加載:

當(dāng)前,我們獲取到的是圖片文件的臨時(shí)路徑,得到圖片路徑以后,接下來(lái)要做的事情,就是如何正確適配的顯示出來(lái)。

圖片尺寸適配

由于要在前端對(duì)圖片進(jìn)行裁剪,使用canvas繪制圖片也是不可少的,在繪制之前,我們需要根據(jù)圖片尺寸進(jìn)行適配處理。

首先需要做的就是讀取圖片的尺寸大小,使用小程序的 wx.getImageInfo 接口即可,它能夠返回圖片原始的寬高屬性。

接著,根據(jù)圖片的寬高屬性、系統(tǒng)屏幕的寬高,一起在小程序頁(yè)面可見(jiàn)區(qū)域內(nèi)設(shè)置圖片的縮放顯示:

// 根據(jù)窗口大小和圖片大小,設(shè)置圖片顯示尺寸以及縮放
// imgHeight 和 imgWidth 表示當(dāng)前圖片的寬高
// 設(shè)置圖片顯示面板的寬高
let panelW = sys.windowWidth - 20
let panelH = sys.windowHeight - 100
if (panelH / panelW >= imgHeight / imgWidth) {
  panelH = parseInt(panelW * imgHeight / imgWidth)
} else {
  panelW = parseInt(panelH * imgWidth / imgHeight)
}
// 圖片在寬高上的縮放比,用于裁剪圖片時(shí)計(jì)算圖片實(shí)際尺寸
xScale = panelW / imgWidth
yScale = panelH / imgHeight
this.setData({
  imagePath,
  // 圖片顯示面板寬高
  panelWidth: panelW,
  panelHeight: panelH,
  // 裁剪框的寬高,初始時(shí)與圖片面板一樣
  clipWidth: panelW,
  clipHeight: panelH,
  // 裁剪的實(shí)際寬高
  croppingImageWidth: imgWidth,
  croppingImageHeight: imgHeight
})

通過(guò)以上代碼和注釋?zhuān)覀冎懒藞D片加載時(shí),需要計(jì)算的幾個(gè)寬高尺寸值,都是比較重要的。

圖片顯示與裁剪框

下面就可以在頁(yè)面上顯示圖片,直接展示圖片顯示區(qū)域的 wxml 代碼:

<view wx:if="{{imagePath}}" class="crop-container">
  <view class="crop-content" style="width: {{panelWidth + 'px'}}; height: {{panelHeight + 'px'}};">
    <image src="{{imagePath}}" class="pos-absolute" style="width: {{panelWidth + 'px'}}; height: {{panelHeight + 'px'}}; left: 0; top:0;"/>
    <view class="mask-bg" style="width: {{panelWidth + 'px'}}; height: {{panelHeight + 'px'}}; left: 0; top: 0;"></view>
    <view class="clip-path" style="width: {{clipWidth + 'px'}}; height: {{clipHeight + 'px'}}; left: {{clipX + 'px'}}; top: {{clipY + 'px'}};">
      <image src="{{imagePath}}" class="pos-absolute" style="width: {{panelWidth + 'px'}}; height: {{panelHeight + 'px'}}; left: {{clipImgX + 'px'}}; top: {{clipImgY + 'px'}};"/>
    </view>
    <view class="clip-box"  bind:touchmove="touchmoveM" bind:touchstart="touchstartM" style="width: {{clipWidth + 'px'}}; height: {{clipHeight + 'px'}}; left: {{clipX + 'px'}}; top: {{clipY + 'px'}};">
      <view capture-catch:touchmove="touchmove" capture-catch:touchstart="touchstart" data-id="leftTop" class="corner-area left-top"></view>
      <view capture-catch:touchmove="touchmove" capture-catch:touchstart="touchstart" data-id="rightTop" class="corner-area right-top"></view>
      <view capture-catch:touchmove="touchmove" capture-catch:touchstart="touchstart" data-id="rightBottom" class="corner-area right-bottom"></view>
      <view capture-catch:touchmove="touchmove" capture-catch:touchstart="touchstart" data-id="leftBottom" class="corner-area left-bottom"></view>
    </view>
  </view>
</view>

以上代碼中,

  • .crop-content外層增加圖片面板,圖片資源直接使用 <image> 組件加載,與外層面板寬高保持一致;
  • .mask-bg增加一個(gè)遮罩層,作為非裁剪區(qū)域的蒙層,以黑灰色透明度實(shí)現(xiàn)效果;
  • .clip-path于圖片面板內(nèi)部,設(shè)置圖片的裁剪區(qū)域,這里使用的是CSS中的 clip-path 屬性,內(nèi)部加載一張圖片,作為裁剪區(qū)域的圖片顯示;
  • clip-box設(shè)置裁剪框區(qū)域的四個(gè)corner角:左上、右上、右下和左下,里面四個(gè)元素對(duì)應(yīng)這個(gè)四個(gè)角,可以對(duì)裁剪框進(jìn)行縮放處理;
    當(dāng)然,我們也可以在各邊的中間位置再加上操作區(qū),那一共就是8個(gè);
  • .clip-path圖片裁剪區(qū)與 clip-box 裁剪框需要設(shè)置位置信息,用于在移動(dòng)的時(shí)候進(jìn)行定位。

通過(guò)設(shè)置對(duì)應(yīng)的圖片元素、蒙層、裁剪框等等,我們就已經(jīng)完成了圖片裁剪的結(jié)構(gòu)布局了,具體可見(jiàn)下圖所示:

圖片裁剪框設(shè)置完成后,我們需要處理的就是裁剪框的拖動(dòng)與縮放事件處理了。

裁剪框的拖動(dòng)與縮放

上面的 wxml 代碼中,在裁剪框的元素部分,已經(jīng)增加了 touchstarttouchmove 等事件,用于在處理拖動(dòng)和縮放等操作。

當(dāng)我們實(shí)現(xiàn)裁剪框的拖動(dòng),只需要如下兩個(gè)事件:

touchstartM (event) {
  const { clipX, clipY } = this.data
  const { pageX, pageY } = event.touches[0]
  // 獲取鼠標(biāo)點(diǎn)在裁剪框的內(nèi)部位置信息
  clipBoxMoveInnerX = pageX - clipX
  clipBoxMoveInnerY = pageY - clipY
},
touchmoveM (event) {
  const { pageX, pageY } = event.touches[0]
  const { panelWidth, panelHeight, clipHeight, clipWidth } = this.data

  // 裁剪框不能脫離面板
  // X位置范圍為 0 到 (面板寬度-裁剪框?qū)挾?
  let clipX = pageX - clipBoxMoveInnerX
  clipX = Math.max(clipX, 0)
  const panelX = panelWidth - clipWidth
  clipX = Math.min(clipX, panelX)
  // Y位置范圍為 0 到 (面板高度-裁剪框高度)
  let clipY = pageY - clipBoxMoveInnerY
  clipY = Math.max(clipY, 0)
  const panleY = panelHeight - clipHeight
  clipY = Math.min(clipY, panleY)

  // 裁剪框底圖位置信息
  const clipImgX = 0 - clipX
  const clipImgY = 0 - clipY

  this.setData({
    clipX,
    clipY,
    clipImgX,
    clipImgY
  })
}

以上代碼,通過(guò)計(jì)算圖片移動(dòng)時(shí)的相對(duì)位移,重新計(jì)算裁剪框的新的位置信息,需要注意的是,移動(dòng)時(shí)不要脫離外層的面板——即不能脫離圖片區(qū)域,否則無(wú)效。

縮放的操作則相對(duì)復(fù)雜一些,需要計(jì)算位移移動(dòng)的距離以及當(dāng)前位置下的裁剪寬高數(shù)據(jù),并且要對(duì)每個(gè)不同的corner角進(jìn)行特殊處理,這塊的完整代碼和注釋如下所示:

// 處理縮放動(dòng)作中不同corner時(shí)的尺寸位置信息
getClipX (clipWidth) {
  switch (activeCorner) {
    case 'leftTop':
    case 'leftBottom':
      return clipBoxBeforeScaleClipX + (clipBoxBeforeScaleWidth - clipWidth)
    case 'rightTop':
    case 'rightBottom':
      return clipBoxBeforeScaleClipX
    default:
      return 0
  }
},
getClipY (clipHeight) {
  switch (activeCorner) {
    case 'leftTop':
    case 'rightTop':
      return clipBoxBeforeScaleClipY + (clipBoxBeforeScaleHeight - clipHeight)
    case 'leftBottom':
    case 'rightBottom':
      return clipBoxBeforeScaleClipY
    default:
      return 0
  }
},
getScaleXWidthOffset (offsetW) {
  switch (activeCorner) {
    case 'leftTop':
    case 'leftBottom':
      return -offsetW
    case 'rightTop':
    case 'rightBottom':
      return offsetW
    default:
      return 0
  }
},
getScaleYHeightOffset (offsetH) {
  switch (activeCorner) {
    case 'rightBottom':
    case 'leftBottom':
      return offsetH
    case 'rightTop':
    case 'leftTop':
      return -offsetH
    default:
      return 0
  }
},
touchstart (event) {
  const dragId = event.currentTarget.dataset.id
  const { pageX, pageY } = event.touches[0]
  const { clipX, clipY, clipHeight, clipWidth } = this.data

  // 設(shè)置縮放時(shí)臨時(shí)變量初始化值
  activeCorner = dragId
  clipBoxBeforeScalePageX = pageX
  clipBoxBeforeScalePageY = pageY
  clipBoxBeforeScaleClipX = clipX
  clipBoxBeforeScaleClipY = clipY
  clipBoxBeforeScaleWidth = clipWidth
  clipBoxBeforeScaleHeight = clipHeight
},
touchmove (event) {
  const { pageX, pageY } = event.touches[0]
  const { panelWidth, panelHeight } = this.data

  // 縮放在X上的偏移
  const xWidthOffset = this.getScaleXWidthOffset(pageX - clipBoxBeforeScalePageX)
  // 裁剪框最小寬度36
  let clipWidth = Math.max(clipBoxBeforeScaleWidth + xWidthOffset, 36)
  // 設(shè)置縮放最大寬度,放大時(shí)不能超過(guò)面板、縮小時(shí)不能超過(guò)初始裁剪框
  let tempPanelWidth = pageX > clipBoxBeforeScalePageX ? panelWidth - clipBoxBeforeScaleClipX : clipBoxBeforeScaleWidth + clipBoxBeforeScaleClipX
  // 設(shè)置裁剪框?qū)挾?
  clipWidth = Math.min(clipWidth, tempPanelWidth)

  // 縮放在Y上的偏移
  const yHeightOffset = this.getScaleYHeightOffset(pageY - clipBoxBeforeScalePageY)
  // 裁剪框最小高度36
  let clipHeight = Math.max(clipBoxBeforeScaleHeight + yHeightOffset, 36)
  // 設(shè)置縮放最大高度,放大時(shí)不能超過(guò)面板、縮小時(shí)不能超過(guò)初始裁剪框
  let tempPanelHeight = pageY > clipBoxBeforeScalePageY ? panelHeight - clipBoxBeforeScaleClipY : clipBoxBeforeScaleHeight + clipBoxBeforeScaleClipY
  // 設(shè)置裁剪框高度
  clipHeight = Math.min(clipHeight, tempPanelHeight)

  // 裁剪框位置信息
  let clipX = this.getClipX(clipWidth)
  let clipY = this.getClipY(clipHeight)
  // 裁剪框底圖位置信息
  let clipImgX = 0 - clipX
  let clipImgY = 0 - clipY

  this.setData({
    clipWidth,
    clipHeight,
    clipX,
    clipY,
    clipImgX,
    clipImgY,
    croppingImageWidth: parseInt(clipWidth / xScale),
    croppingImageHeight: parseInt(clipHeight / yScale)
  })
}

至此,圖片裁剪的功能基本完成了,能夠加載圖片、設(shè)置裁剪框、拖動(dòng)和縮放裁剪框大小,計(jì)算裁剪圖片的尺寸等等。接下來(lái),就剩下如何真正剪裁圖片了。

增加canvas并裁剪圖片

要剪裁圖片,我們?cè)谛〕绦蚴褂胏anvas畫(huà)布組件來(lái)處理,在頁(yè)面上增加一個(gè)canvas元素:

<canvas id="main" canvasId="main" class="main-canvas" style="width: {{croppingImageWidth + 'px'}}; height: {{croppingImageHeight + 'px'}}"></canvas>

由于這個(gè)canvas只是用來(lái)對(duì)圖片進(jìn)行裁剪操作,并不需要顯示出來(lái),我們可以把它定位到視覺(jué)窗口以外,不影響可操作的界面元素。

給畫(huà)布設(shè)置圖片裁剪所需要的寬高,通過(guò)在同等寬高下繪制圖片,就能導(dǎo)出圖片:

wx.showLoading({ title: '正在裁剪...' })
preCtx.clearRect(0, 0, imageWidth, imageHeight)
const width = croppingImageWidth
const height = croppingImageHeight
const xPos = Math.abs(clipImgX / xScale)
const yPos = Math.abs(clipImgY / yScale)
// 繪制裁剪區(qū)內(nèi)的圖片到畫(huà)布上
preCtx.drawImage(imagePath, xPos, yPos, width, height, 0, 0, width, height)
preCtx.save()
preCtx.restore()
const that = this
preCtx.draw(false, function () {
  setTimeout(() => {
    // 將畫(huà)布導(dǎo)出為臨時(shí)圖片文件
    wx.canvasToTempFilePath({
      x: 0,
      y: 0,
      width,
      height,
      destWidth: width,
      destHeight: height,
      canvasId: 'main',
      success: (canRes) => {
        wx.hideLoading()
        that.initImage(width, height, canRes.tempFilePath)
      },
      fail (err) {
        wx.hideLoading()
        console.log(err)
      }
    })
  }, 200)
})

如上代碼所示,通過(guò)裁剪圖片的真實(shí)寬高以及相對(duì)位置信息,通過(guò)canvas的 drawImage 方法,把圖片的裁剪區(qū)域的內(nèi)容繪制到畫(huà)布上,此時(shí),該畫(huà)布就對(duì)應(yīng)一張裁剪后的圖片,我們只需要把畫(huà)布導(dǎo)出成圖片文件即可。

導(dǎo)出畫(huà)布,使用 wx.canvasToTempFilePath 方法,用于將畫(huà)布導(dǎo)出為圖片臨時(shí)圖片文件,這個(gè)圖片文件可以重新加載或者進(jìn)行導(dǎo)出。

保存圖片到相冊(cè)

以上過(guò)程,生成裁剪圖片的臨時(shí)文件后,就可以使用小程序提供的API,將圖片文件保存到相冊(cè)中。

只需要使用 wx.saveImageToPhotosAlbum 方法,專(zhuān)門(mén)用于將圖片文件保存到系統(tǒng)相冊(cè),接收臨時(shí)文件作為參數(shù):

wx.saveImageToPhotosAlbum({
  filePath: imgSrc,
  success: () => {
    wx.hideLoading()
    wx.vibrateShort()
    wx.showModal({
      content: '圖片已保存到相冊(cè)~',
      showCancel: false,
      confirmText: '好的',
      confirmColor: '#333'
    })
  }
})

該方法簡(jiǎn)單方便,其中使用 wx.vibrateShort() 方法,作用是使手機(jī)發(fā)生較短時(shí)間的振動(dòng)(15 ms),在小程序也是常見(jiàn)的功能。

圖片保存到系統(tǒng)相冊(cè)功能完成后,我們就實(shí)現(xiàn)了在小程序中進(jìn)行圖片剪裁的完整功能,包含加載圖片、圖片適配和裁剪框繪制、裁剪框拖動(dòng)與縮放事件、圖片導(dǎo)出和保存的過(guò)程。

總結(jié)

本文詳細(xì)講述了在小程序中實(shí)現(xiàn)一個(gè)圖片裁剪功能的過(guò)程,可以看出和在瀏覽器Web端的實(shí)現(xiàn)差別并不大。

在核心的圖片適配、裁剪框繪制與縮放事件處理上,基本兩邊可以通用,在小程序中的實(shí)現(xiàn)邏輯可以完整在移到web瀏覽器上,反之亦然。

區(qū)別可能只只在于圖片的加載和保存上,可以使用小程序提供的多種內(nèi)置接口方法,能較方便的完成。

上文也附上了大量的核心代碼,根據(jù)這些代碼已經(jīng)基本可以還原裁剪功能。

以上就是JavaScript實(shí)現(xiàn)小程序圖片裁剪功能的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于JavaScript圖片裁剪功能的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 詳解iframe跨域的幾種常用方法(小結(jié))

    詳解iframe跨域的幾種常用方法(小結(jié))

    這篇文章主要介紹了詳解iframe跨域的幾種常用方法(小結(jié)),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
    2019-04-04
  • ES6中的WeakMap和WeakSet特性和用途詳解

    ES6中的WeakMap和WeakSet特性和用途詳解

    在JavaScript的ES6版本中,引入了WeakMap和WeakSet這兩種新的數(shù)據(jù)結(jié)構(gòu),與Map和Set相比,它們有一些特殊的特點(diǎn)和用途,使它們?cè)谔幚韮?nèi)存泄漏問(wèn)題、管理對(duì)象私有數(shù)據(jù)、處理對(duì)象的唯一性等場(chǎng)景中有顯著的優(yōu)勢(shì),本文將深入探討WeakMap和WeakSet的特性和用途,一起看看吧
    2023-12-12
  • 微信小程序用戶自定義模版用法實(shí)例分析

    微信小程序用戶自定義模版用法實(shí)例分析

    這篇文章主要介紹了微信小程序用戶自定義模版用法,結(jié)合實(shí)例形式較為詳細(xì)的分析了微信小程序自定義模板的定義、數(shù)據(jù)調(diào)用、布局設(shè)置等簡(jiǎn)單使用技巧,需要的朋友可以參考下
    2017-11-11
  • js實(shí)現(xiàn)仿購(gòu)物車(chē)加減效果

    js實(shí)現(xiàn)仿購(gòu)物車(chē)加減效果

    本文主要介紹了js實(shí)現(xiàn)仿購(gòu)物車(chē)+ -效果的實(shí)例,具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-03-03
  • JavaScript 計(jì)算圖片加載數(shù)量的代碼

    JavaScript 計(jì)算圖片加載數(shù)量的代碼

    先定義一個(gè)圖片的數(shù)組,然后通過(guò)image的onload事件來(lái)計(jì)算,注意,onload在ie和火狐有所不同。
    2011-01-01
  • JavaScript獲得指定對(duì)象大小的方法

    JavaScript獲得指定對(duì)象大小的方法

    這篇文章主要介紹了JavaScript獲得指定對(duì)象大小的方法,涉及javascript針對(duì)元素遍歷與屬性操作的相關(guān)技巧,需要的朋友可以參考下
    2015-07-07
  • ES6入門(mén)教程之Array.from()方法

    ES6入門(mén)教程之Array.from()方法

    這篇文章主要給大家介紹了關(guān)于ES6入門(mén)教程之Array.from()方法的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用ES6具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-03-03
  • window.showModalDialog參數(shù)傳遞中含有特殊字符的處理方法

    window.showModalDialog參數(shù)傳遞中含有特殊字符的處理方法

    程序運(yùn)行出錯(cuò)經(jīng),過(guò)檢查發(fā)現(xiàn)傳遞的數(shù)據(jù)中出現(xiàn)了#等特殊字符,瀏覽器只取到#號(hào)前面的數(shù)據(jù),后面的被截?cái)?,下面為大家介紹下正確的處理方法
    2013-06-06
  • 單擊瀏覽器右上角的X關(guān)閉窗口彈出提示的小例子

    單擊瀏覽器右上角的X關(guān)閉窗口彈出提示的小例子

    單擊瀏覽器右上角的X關(guān)閉窗口彈出提示的小例子,需要的朋友可以參考一下
    2013-06-06
  • layui-table表復(fù)選框勾選的所有行數(shù)據(jù)獲取的例子

    layui-table表復(fù)選框勾選的所有行數(shù)據(jù)獲取的例子

    今天小編就為大家分享一篇layui-table表復(fù)選框勾選的所有行數(shù)據(jù)獲取的例子,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧
    2019-09-09

最新評(píng)論