canvas離屏技術與放大鏡實現

来源:https://www.cnblogs.com/geyouneihan/archive/2018/08/30/9562472.html
-Advertisement-
Play Games

教程所示圖片使用的是 github 倉庫圖片,網速過慢的朋友請移步 " (原文)canvas 離屏技術與放大鏡實現" 。 更多討論或者錯誤提交,也請移步。 利用 除了可以實現濾鏡,還可以利用 離屏技術 放大鏡功能。 為了方便講解,本文分為 2 個應用部分: 1. 實現水印和中心縮放 2. 實現放大鏡 ...


教程所示圖片使用的是 github 倉庫圖片,網速過慢的朋友請移步>>> (原文)canvas 離屏技術與放大鏡實現

更多討論或者錯誤提交,也請移步。

利用canvas除了可以實現濾鏡,還可以利用離屏技術放大鏡功能。

為了方便講解,本文分為 2 個應用部分:

  1. 實現水印和中心縮放
  2. 實現放大鏡

1. 什麼是離屏技術?

canvas 學習和濾鏡實現介紹過drawImage介面。除了繪製圖像,這個介面還可以:將一個canvas對象繪製到另一個canvas對象上。這就是離屏技術。

2. 實現水印和中心縮放

在代碼中,有兩個 canvas 標簽。分別是可見與不可見。不可見的 canvas 對象上的 Context 對象,就是我們放置圖像水印的地方。

更多詳解,請看代碼註釋:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Learn Canvas</title>
  <style>
    canvas {
      display: block;
      margin: 0 auto;
      border: 1px solid #222;
    }

    input {
      display: block;
      margin: 20px auto;
      width: 800px
    }
  </style>
</head>
<body>
  <div id="app">
    <canvas id="my-canvas"></canvas>
    <input type="range" value="1.0" min="0.5" max="3.0" step="0.1">
    <canvas id="watermark-canvas" style="display: none;"></canvas>
  </div>
  <script type="text/javascript">
    window.onload = function () {
      var canvas = document.querySelector("#my-canvas")
      var watermarkCanvas = document.querySelector("#watermark-canvas")
      var slider = document.querySelector("input")

      var scale = slider.value

      var ctx = canvas.getContext('2d')
      var watermarkCtx = watermarkCanvas.getContext("2d")

      /* 給第二個canvas獲取的Context對象添加水印 */
      watermarkCanvas.width = 300
      watermarkCanvas.height = 100
      watermarkCtx.font = "bold 20px Arial"
      watermarkCtx.lineWidth = "1"
      watermarkCtx.fillStyle = "rgba(255 , 255 , 255, 0.5)"
      watermarkCtx.fillText("=== yuanxin.me ===", 50, 50)
      /****************************************/

      var img = new Image()
      img.src = "./img/photo.jpg"

      /* 載入圖片後執行操作 */
      img.onload = function () {
        canvas.width = img.width;
        canvas.height = img.height;
        drawImageByScale(canvas, ctx, img, scale, watermarkCanvas);
        // 監聽input標簽的mousemove事件
        // 註意:mousemove實時監聽值的變化,記憶體消耗較大
        slider.onmousemove = function () {
          scale = slider.value
          drawImageByScale(canvas, ctx, img, scale, watermarkCanvas);
        }
      }
      /******************/
    }
    /**
    *
    * @param {Object} canvas 畫布對象
    * @param {Object} ctx
    * @param {Object} img
    * @param {Number} scale 縮放比例
    * @param {Object} watermark 水印對象
    */
    function drawImageByScale(canvas, ctx, img, scale, watermark) {
      // 圖像按照比例進行縮放
      var width = img.width * scale,
        height = img.height * scale
      // (dx, dy): 畫布上繪製img的起始坐標
      var dx = canvas.width / 2 - width / 2,
        dy = canvas.height / 2 - height / 2
      ctx.clearRect(0, 0, canvas.width, canvas.height) // No1 清空畫布
      ctx.drawImage(img, dx, dy, width, height) // No2 重新繪製圖像
      if (watermark) {
        // No3 判斷是否有水印: 有, 繪製水印
        ctx.drawImage(watermark, canvas.width - watermark.width, canvas.height - watermark.height)
      }
    }
  </script>
</body>
</html>

實現效果如下圖所示:

拖動滑竿,即可放大和縮小圖像。然後右鍵保存圖像。保存後的圖像,就有已經有了水印,如下圖所示:

3. 實現放大鏡

在上述中心縮放的基礎上,實現放大鏡主需要註意以下 2 個部分:

  1. 細化處理canvas的滑鼠響應事件:滑入、滑出、點擊和鬆開
  2. 重新計算離屏坐標(詳細公式計算思路請見代碼註釋)
  3. 重新計算滑鼠相對於 canvas 標簽的坐標(詳細公式計算思路請見代碼註釋)

代碼如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Document</title>
  <style>
    canvas {
      display: block;
      margin: 0 auto;
      border: 1px solid #222;
    }
  </style>
</head>
<body>
  <canvas id="my-canvas"></canvas>
  <canvas id="off-canvas" style="display: none;"></canvas>
  <script>
    var isMouseDown = false,
      scale = 1.0
    var canvas = document.querySelector("#my-canvas")
    var offCanvas = document.querySelector("#off-canvas") // 離屏 canvas
    var ctx = canvas.getContext("2d")
    var offCtx = offCanvas.getContext("2d") // 離屏 canvas 的 Context對象
    var img = new Image()

    window.onload = function () {
      img.src = "./img/photo.jpg"

      img.onload = function () {
        canvas.width = img.width
        canvas.height = img.height

        offCanvas.width = img.width
        offCanvas.height = img.height

        // 計算縮放比例
        scale = offCanvas.width / canvas.width

        // 初識狀態下, 兩個canvas均繪製Image
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
        offCtx.drawImage(img, 0, 0, canvas.width, canvas.height)

      }

      // 滑鼠按下
      canvas.onmousedown = function (event) {
        event.preventDefault() // 禁用預設事件
        var point = windowToCanvas(event.clientX, event.clientY) // 獲取滑鼠相對於 canvas 標簽的坐標
        isMouseDown = true
        drawCanvasWithMagnifier(true, point) // 繪製在離屏canvas上繪製放大後的圖像
      }

      // 滑鼠移動
      canvas.onmousemove = function (event) {
        event.preventDefault() // 禁用預設事件
        if (isMouseDown === true) {
          var point = windowToCanvas(event.clientX, event.clientY)
          drawCanvasWithMagnifier(true, point)
        }
      }

      // 滑鼠鬆開
      canvas.onmouseup = function (event) {
        event.preventDefault() // 禁用預設事件
        isMouseDown = false
        drawCanvasWithMagnifier(false) // 不繪製離屏放大鏡
      }

      // 滑鼠移出canvas標簽
      canvas.onmouseout = function (event) {
        event.preventDefault() // 禁用預設事件
        isMouseDown = false
        drawCanvasWithMagnifier(false) // 不繪製離屏放大鏡
      }
    }

    /**
    * 返回滑鼠相對於canvas左上角的坐標
    * @param {Number} x 滑鼠的屏幕坐標x
    * @param {Number} y 滑鼠的屏幕坐標y
    */
    function windowToCanvas(x, y) {
      var bbox = canvas.getBoundingClientRect() // bbox中存儲的是canvas相對於屏幕的坐標
      return {
        x: x - bbox.x,
        y: y - bbox.y
      }
    }

    function drawCanvasWithMagnifier(isShow, point) {
      ctx.clearRect(0, 0, canvas.width, canvas.height) // 清空畫布
      ctx.drawImage(img, 0, 0, canvas.width, canvas.height) // 在畫布上繪製圖像

      /* 利用離屏,繪製放大鏡 */
      if (isShow) {
        var { x, y } = point

        var mr = 50 // 正方形放大鏡邊長

        // (sx, sy): 待放大圖像的開始坐標
        var sx = x - mr / 2,
          sy = y - mr / 2

        // (dx, dy): 已放大圖像的開始坐標
        var dx = x - mr,
          dy = y - mr

        // 將offCanvas上的(sx,sy)開始的長寬均為mr的正方形區域
        // 放大到
        // canvas上的(dx,dy)開始的長寬均為 2 * mr 的正方形可視區域
        // 由此實現放大效果
        ctx.drawImage(offCanvas, sx, sy, mr, mr, dx, dy, 2 * mr, 2 * mr)
      }
      /*********************/
    }
  </script>
</body>
</html>

放大鏡效果如下圖所示(被紅筆標出的區域就是我們的正方形放大鏡):

歡迎入群:857989948 。IT 技術深度交流和分享,涉及方麵包括但不限於:網站製作、運營、UI 設計、演算法分析、大數據、人工智慧等。本群主打有深度、有態度的技術交流,歡迎熱衷記錄知識的您的加入。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 大部分APP必備需求,使用總結 Android接入文章在此:官方文檔 文檔很簡單,Android分為四步: 1、後臺配置 2、Android 內 註冊appId 3、Android 內 調起支付 4、Android 內 支付結果回調 詳細流程總結: 1、後臺配置 這個讓伺服器同事去做就行,記得把項目 ...
  • 首先我們來說說什麼是跨域 跨域:是指從一個功能變數名稱的網頁去請求另一個功能變數名稱的資源。比如從www.baidu.com 頁面去請求 www.google.com 的資源。但是一般情況下不能這麼做,它是由瀏覽器的同源策略造成的 這裡我們又會有一個問題 啥叫同源策略啊,那我們再來說說同源策略, 首先什麼叫同源呢 ...
  • 作為前端最火的構建工具,是前端自動化工具鏈 最重要的部分 ,使用門檻較高。本系列是筆者自己的學習記錄,比較基礎,希望通過 問題 + 解決方式 的模式,以前端構建中遇到的具體需求為出發點,學習 工具中相應的處理辦法。(本篇中的參數配置及使用方式均基於 ) 一. loader綜述 是`webpack l ...
  • JavaScript概述 1、ECMAScript和JavaScript的關係 1996年11月,JavaScript的創造者--Netscape公司,決定將JavaScript提交給國際標準化組織ECMA,希望這門語言能夠成為國際標準。次年,ECMA發佈262號標準文件(ECMA-262)的第一版 ...
  • npm ERR! Cannot read property 'path' of null ...
  • aangularjs指令的作用域,通過scope來實現,scope有三種情況的值:true、fasle、{}。預設值為true。其一、scope=false:和父級完全共用一個作用域;其二、scope=true: 創建了一個新的 作用域,初始化時繼承父作用域。 ...
  • 前端開發者很容易暴露自己的請求地址和參數,我們都知道,一個h5頁面,按 F12 是可以看到頁面的源碼的,所以經常很多人會利用這一點惡意調取別人的介面。 我們公司出現了好多次簡訊介面被大量調用,導致一天發了幾萬條簡訊,正常來說一天就幾百條,這期間浪費了那麼多條簡訊。今天,我又發現有人惡意調我們公司的接 ...
  • webpack4.0各個擊破(5)—— Module篇 作為前端最火的構建工具,是前端自動化工具鏈 最重要的部分 ,使用門檻較高。本系列是筆者自己的學習記錄,比較基礎,希望通過 問題 + 解決方式 的模式,以前端構建中遇到的具體需求為出發點,學習 工具中相應的處理辦法。(本篇中的參數配置及使用方式均 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...