最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

JS使用canvas實(shí)現(xiàn)基本的截圖功能

 更新時(shí)間:2023年08月09日 10:03:22   作者:寄給江南  
這篇文章主要給大家介紹了使用JS中的canvas實(shí)現(xiàn)基本的截圖功能,文中有詳細(xì)的實(shí)現(xiàn)思路和實(shí)現(xiàn)過(guò)程,文章通過(guò)代碼示例講解的非常詳細(xì),很感興趣的同學(xué)可以參考一下

思路分析

在開(kāi)始動(dòng)手之前,分析一下整個(gè)功能的實(shí)現(xiàn)過(guò)程:

  • 根據(jù)圖片大小創(chuàng)建 canvas1 畫布,并將原圖片直接定位在 canvas1 上;

  • 在畫布上添加一個(gè)蒙層,以區(qū)分當(dāng)前 canvas 圖像是被裁剪的原圖像;

  • 在蒙層上方,對(duì)裁剪區(qū)域(鼠標(biāo)移動(dòng)形成的矩形范圍)再次進(jìn)行圖像繪制;

  • 獲取裁剪區(qū)域的數(shù)據(jù),并將該數(shù)據(jù)定位到另一個(gè) canvas 畫布上。

實(shí)現(xiàn)過(guò)程

準(zhǔn)備工作

首先,編寫所需的 HTML 結(jié)構(gòu)并獲取對(duì)應(yīng)元素。

<body>
  <!-- 上傳文件 -->
  <input type="file" id="imageFile" accept="image/*">
  <!-- 保存被裁剪的原圖像,初始樣式需要設(shè)置 display: none -->
  <div class="canvasContainer1">
    <canvas id="canvas1"></canvas>
  </div>
  <!-- 保存裁剪區(qū)域的圖像,初始樣式需要設(shè)置 display: none -->
  <div class="canvasContainer2">
    <canvas id="canvas2"></canvas>
  </div>
</body>
<script>
const imageFile = document.querySelector('#imageFile');
const canvasContainer1 = document.querySelector('.canvasContainer1');
const canvasContainer2 = document.querySelector('.canvasContainer2');
const canvas1 = document.querySelector('#canvas1');
const canvas2 = document.querySelector('#canvas2');
const ctx = canvas1.getContext('2d');
const ctx2 = canvas2.getContext('2d');
const imageBox = new Image(); // 創(chuàng)建一個(gè)存放圖片的容器
</script>

繪制原圖像

我們需要監(jiān)聽(tīng) input 元素的 change 事件,以獲取上傳圖片的相關(guān)參數(shù),這里主要是為了獲取圖片的寬度和高度。

我們創(chuàng)建一個(gè) FileReader() 對(duì)象并監(jiān)聽(tīng)其 load 事件。load 事件在讀取操作成功后立刻執(zhí)行,在這個(gè)方法中我們就可以獲取圖片的寬高。

function init() {
  imageFile.addEventListener('change', handleFileChange, false); // 監(jiān)聽(tīng)圖片上傳事件。
}
function handleFileChange(e) {
  const imgFile = e.target.files[0]; // 獲取上傳的圖片對(duì)象。
  const reader = new FileReader();
  reader.onload = function(e) {
    const imgSrc = e.target.result; // 圖片文件的 base64 編碼格式。
    imageBox.src = imgSrc; // 把圖片放入 img 容器。
    // 等圖片加載完成后,獲取圖片的寬高。
    imageBox.onload = function () {
      const imgWidth = this.width, imgHeight = this.height;
      console.log(imgWidth, imgHeight);
    }
  }
  if (imgFile) {
    reader.readAsDataURL(imgFile); // 讀取圖片文件,讀取完成才能獲取 result 屬性。
  }
}
init();

此時(shí)還沒(méi)有圖片,我們創(chuàng)建一個(gè)自適應(yīng)圖片大小的 canvas1 畫布,并使用 drawImage() 方法將上傳的圖片直接定位到 canvas1 當(dāng)中。

function handleFileChange(e) {
  const imgFile = e.target.files[0]; // 獲取上傳的圖片對(duì)象。
  const reader = new FileReader();
  reader.onload = function (e) {
    const imgSrc = e.target.result; // 圖片的 base64 編碼。
    imageBox.src = imgSrc; // 把上傳的圖像放入 img 容器。
    // 圖片加載完畢后執(zhí)行
    imageBox.onload = function () {
      // 獲取圖片的寬高。
      const imgWidth = this.width, imgHeight = this.height;
      console.log(imgWidth, imgHeight);
      // 創(chuàng)建 canvas 畫布并繪制圖片。
      generateCanvas(canvasContainer1, canvas1, imgWidth, imgHeight);
      ctx.drawImage(imageBox, 0, 0, imgWidth, imgHeight);
    }
  }
  if (imgFile) {
    reader.readAsDataURL(imgFile); // 將當(dāng)前file讀取成DataURL
  }
}
// 根據(jù) width 和 height 創(chuàng)建 canvas 畫布。
function generateCanvas(container, canvas, width, height) {
  container.width = width + 'px';
  container.height = height + 'px';
  canvas.width = width;
  canvas.height = height;
  container.style.display = 'block'; // 顯示 canvas 區(qū)域。
}

可以看到原圖像已經(jīng)成功被繪制,接下來(lái)就可以開(kāi)始動(dòng)態(tài)繪制截圖區(qū)域了。

繪制截圖區(qū)域

在這個(gè)過(guò)程中,我們需要分別監(jiān)聽(tīng) imageBox 容器(原圖像)上的 mousedown、mousemovemouseup 事件,這些事件的作用如下:

  • mousedown 事件:記錄開(kāi)始截圖的位置,并開(kāi)始監(jiān)聽(tīng) mousemovemouseup 事件。
  • mousemove 事件:監(jiān)聽(tīng)鼠標(biāo)的偏移量,以計(jì)算裁剪區(qū)域的寬度和高度。
  • mouseup 事件:截圖結(jié)束,注銷監(jiān)聽(tīng) mousedownmousemove 事件,并繪制裁剪區(qū)域。
let startPosition = []; // 記錄鼠標(biāo)點(diǎn)擊(開(kāi)始截圖)的位置。
let screenshotData = []; // 保存截取部分的相關(guān)信息。
function init() {
  // 監(jiān)聽(tīng)鼠標(biāo)點(diǎn)擊事件。
  canvas1.addEventListener('mousedown', handleMouseDown, false);
}
// 記錄鼠標(biāo)點(diǎn)擊(開(kāi)始截圖)的位置,并監(jiān)聽(tīng)相關(guān)事件。
function handleMouseDown(e) {
  startPosition = [e.offsetX, e.offsetY];
  canvas1.addEventListener('mousemove', handleMouseMove, false);
  canvas1.addEventListener('mouseup', handleMouseUp, false);
}
// 監(jiān)聽(tīng)鼠標(biāo)的偏移量,以計(jì)算裁剪區(qū)域的寬度和高度。
function handleMouseMove(e) {
  // 獲取裁剪區(qū)域的寬度和高度。
  const { offsetX, offsetY } = e;
  const [startX, startY] = startPosition;
  const [rectWidth, rectHeight] = [offsetX - startX, offsetY - startY];
  console.log('rect', rectWidth, rectHeight);
  // 保存裁剪區(qū)域的相關(guān)信息。
  screenshotData = [startX, startY, rectWidth, rectHeight];
}
// 注銷監(jiān)聽(tīng)事件等后續(xù)操作。
function handleMouseUp() {
  canvas1.removeEventListener('mousemove', handleMouseMove, false);
  canvas1.removeEventListener('mouseup', handleMouseUp, false);
}

在 handleMouseMove 函數(shù)中,我們已經(jīng)獲取了裁剪區(qū)域的寬高,也就是生成截圖的寬高。

接下來(lái),我們需要在原圖像上展示出我們所裁剪的區(qū)域,也就是這個(gè)效果:

可以看到,原圖像的上方、裁剪區(qū)域下方會(huì)覆蓋一層半透明黑色蒙層,它的作用是區(qū)分原圖層和裁剪部分圖層。所以我們需要在繪制截圖區(qū)域之前,添加一層蒙層。

注意,在已有內(nèi)容的 canvas 畫布上進(jìn)行再次繪制之前,需要先清除整個(gè)畫布的內(nèi)容。 這里通過(guò) clearRect() 方法清除 canvas1 畫布上的所有內(nèi)容,并添加蒙層。

我們繼續(xù)來(lái)補(bǔ)充 handleMouseMovehandleMouseUp 函數(shù)中的邏輯:

const MASKER_OPACITY = 0.4;
function handleMouseMove(e) {
  // 獲取裁剪區(qū)域的寬度和高度。
  const { offsetX, offsetY } = e;
  const [startX, startY] = startPosition;
  const [rectWidth, rectHeight] = [offsetX - startX, offsetY - startY];
  console.log('rect', rectWidth, rectHeight);
  // 保存裁剪區(qū)域的相關(guān)信息。
  screenshotData = [startX, startY, rectWidth, rectHeight];
  // 再次繪制前,清理 canvas1 畫布上的內(nèi)容。
  const { width, height } = canvas1;
  ctx.clearRect(0, 0, width, height);
  // 在 canvas1 畫布上繪制蒙層。
  drawImageMasker(0, 0, width, height, MASKER_OPACITY);
  // 繪制截圖區(qū)域。
  drawScreenShot(width, height, rectWidth, rectHeight);
}
// ...
// 繪制圖片蒙層,填充范圍和顏色,以便區(qū)分原圖層和裁剪部分圖層。
function drawImageMasker(x, y, width, height, opacity) {
  ctx.fillStyle = `rgba(0, 0, 0, ${opacity})`;
  ctx.fillRect(0, 0, width, height);
}
// 繪制裁剪的矩形區(qū)域。
function drawScreenShot(canWidth, canHeight, rectWidth, rectHeight) {
  // 在源圖像外繪制新圖像,只有源圖像外的目標(biāo)圖像部分會(huì)被顯示,源圖像是透明的。
  ctx.globalCompositeOperation = 'destination-out';
  ctx.fillStyle = '#2c2c2c';
  ctx.fillRect(...startPosition, rectWidth, rectHeight);
  // 在現(xiàn)有畫布上繪制新的圖形。
  ctx.globalCompositeOperation = 'destination-over';
  ctx.drawImage(imageBox, 0, 0, canWidth, canHeight, 0, 0, canWidth, canHeight);
}

然后,當(dāng)我們放開(kāi)鼠標(biāo)(結(jié)束截圖動(dòng)作)時(shí),除了注銷對(duì) mousedownmousemove 事件的監(jiān)聽(tīng),還需要將所得的裁剪區(qū)域的圖像放入另一個(gè) canvas 中。

在繪制新圖像的過(guò)程中,我們需要使用以下方法:

  • getImageData():讀取 canvas 上的內(nèi)容,返回一個(gè) ImageData 對(duì)象,包含了每個(gè)像素的信息。
  • putImageData():將 ImagaData 對(duì)象的數(shù)據(jù)放入 canvas 中,覆蓋 canvas 中的已有圖像。
function handleMouseUp() {
  canvas1.removeEventListener('mousemove', handleMouseMove, false);
  canvas1.removeEventListener('mouseup', handleMouseUp, false);
  // 開(kāi)始繪制截圖區(qū)域圖片。
  drawScreenshotImage(screenshotData);
  // 如果裁剪得到新圖像后,不希望保留原圖像,可以設(shè)置以下屬性。
  // canvasContainer1.style.display = 'none';
}
// 在新容器 canvas2 上繪制新圖像。
function drawScreenshotImage(screenshotData) {
  // 獲取 canvas1 的數(shù)據(jù)。
  const data = ctx.getImageData(...screenshotData);
  // 創(chuàng)建 canvas2 畫布。
  generateCanvas(canvasContainer2, canvas2, screenshotData[2], screenshotData[3]);
  // 每次繪制前,都先進(jìn)行清除操作。
  ctx2.clearRect(...screenshotData);
  // 將 canvas1 的數(shù)據(jù)放入 canvas2 中。
  ctx2.putImageData(data, 0, 0);
}

經(jīng)過(guò)以上步驟,就可以實(shí)現(xiàn)我們所需的效果

總結(jié)

對(duì)于 canvas 的使用比較少,所以想記錄一下自己的使用過(guò)程。

如果本文中出現(xiàn)了什么錯(cuò)誤或者有什么建議,歡迎大家指正~

以上就是JS使用canvas實(shí)現(xiàn)基本的截圖功能的詳細(xì)內(nèi)容,更多關(guān)于JS canvas截圖的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • js實(shí)現(xiàn)帶圓角的兩級(jí)導(dǎo)航菜單效果代碼

    js實(shí)現(xiàn)帶圓角的兩級(jí)導(dǎo)航菜單效果代碼

    這篇文章主要介紹了js實(shí)現(xiàn)帶圓角的兩級(jí)導(dǎo)航菜單效果代碼,涉及javascript鼠標(biāo)事件及頁(yè)面元素樣式動(dòng)態(tài)變換技巧,非常具有實(shí)用價(jià)值,需要的朋友可以參考下
    2015-08-08
  • 微信小程序 列表的上拉加載和下拉刷新的實(shí)現(xiàn)

    微信小程序 列表的上拉加載和下拉刷新的實(shí)現(xiàn)

    本文主要介紹了微信小程序中實(shí)現(xiàn)列表的上拉加載和下拉刷新的方法。具有很好的參考價(jià)值。下面跟著小編一起來(lái)看下吧
    2017-04-04
  • js實(shí)現(xiàn)簡(jiǎn)易的英漢詞典

    js實(shí)現(xiàn)簡(jiǎn)易的英漢詞典

    這篇文章主要為大家詳細(xì)介紹了js實(shí)現(xiàn)簡(jiǎn)易的英漢詞典,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2021-09-09
  • JavaScript 繼承詳解 第一篇

    JavaScript 繼承詳解 第一篇

    幾乎每個(gè)開(kāi)發(fā)人員都有面向?qū)ο笳Z(yǔ)言(比如C++、C#、Java)的開(kāi)發(fā)經(jīng)驗(yàn)。 在傳統(tǒng)面向?qū)ο蟮恼Z(yǔ)言中,有兩個(gè)非常重要的概念 - 類和實(shí)例。
    2009-08-08
  • 微信小程序onShareTimeline()實(shí)現(xiàn)分享朋友圈

    微信小程序onShareTimeline()實(shí)現(xiàn)分享朋友圈

    這篇文章主要給大家介紹了關(guān)于微信小程序onShareTimeline()實(shí)現(xiàn)分享朋友圈的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-01-01
  • javascript控制在光標(biāo)位置插入文字適合表情的插入

    javascript控制在光標(biāo)位置插入文字適合表情的插入

    使用javascript控制在光標(biāo)位置插入文字,在實(shí)現(xiàn)表情的插入時(shí)會(huì)用到的,需要的朋友可以參考下
    2014-06-06
  • 前端echarts tooltip展示多項(xiàng)自定義數(shù)據(jù)代碼示例

    前端echarts tooltip展示多項(xiàng)自定義數(shù)據(jù)代碼示例

    Echarts是一個(gè)基于JavaScript的開(kāi)源圖表庫(kù),提供了豐富的圖表類型,如折線圖、柱狀圖、餅圖、散點(diǎn)圖等,支持動(dòng)態(tài)數(shù)據(jù)交互和自定義配置,這篇文章主要給大家介紹了關(guān)于前端echarts tooltip展示多項(xiàng)自定義數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下
    2024-09-09
  • JavaScript如何實(shí)現(xiàn)圖片懶加載(lazyload) 提高用戶體驗(yàn)(增強(qiáng)版)

    JavaScript如何實(shí)現(xiàn)圖片懶加載(lazyload) 提高用戶體驗(yàn)(增強(qiáng)版)

    這篇文章主要介紹了JavaScript如何實(shí)現(xiàn)圖片懶加載(lazyload) 提高用戶體驗(yàn)(增強(qiáng)版)的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下
    2016-11-11
  • js實(shí)現(xiàn)獲取當(dāng)前時(shí)間是本月第幾周的方法

    js實(shí)現(xiàn)獲取當(dāng)前時(shí)間是本月第幾周的方法

    這篇文章主要介紹了js實(shí)現(xiàn)獲取當(dāng)前時(shí)間是本月第幾周的方法,涉及javascript針對(duì)日期及時(shí)間的相關(guān)操作技巧,非常簡(jiǎn)單實(shí)用,需要的朋友可以參考下
    2015-08-08
  • echarts實(shí)現(xiàn)雷達(dá)圖的詳細(xì)步驟

    echarts實(shí)現(xiàn)雷達(dá)圖的詳細(xì)步驟

    這篇文章主要給大家介紹了關(guān)于echarts實(shí)現(xiàn)雷達(dá)圖的詳細(xì)步驟,雷達(dá)圖(Radar?Chart)是一種信息豐富的可視化工具,其中多個(gè)變量(三個(gè)或更多)在二維平面上進(jìn)行比較,文中給出了完整的代碼示例,需要的朋友可以參考下
    2024-01-01

最新評(píng)論

宁武县| 中宁县| 咸丰县| 汪清县| 丰顺县| 阿拉善左旗| 溆浦县| 大理市| 泰来县| 杭锦后旗| 千阳县| 顺义区| 丰台区| 和林格尔县| 交城县| 扎赉特旗| 清涧县| 虹口区| 台前县| 武平县| 突泉县| 搜索| 正安县| 广元市| 宿州市| 长武县| 康定县| 河北省| 百色市| 木兰县| 郸城县| 神池县| 合肥市| 永川市| 固原市| 拉萨市| 昌邑市| 会理县| 德化县| 峨眉山市| 德庆县|