如何使用JavaScript旋转图像

有时我们需要旋转网页上的图像,比如显示正在加载中的动画。在文档扫描Web应用中,我们需要旋转倾斜的或扫描方向错误的文档图像。

在本文中,我们将讨论使用JavaScript旋转图像的三种方法:

编写一个HTML5页面以旋转图像

让我们编写一个HTML5页面来旋转图像。

新建HTML文件

创建一个包含以下模板的新HTML文件。

html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Rotate an Image with JavaScript</title>
  <style>
  .home {
    display: flex;
    align-items: center;
    flex-direction: column;
  }
  </style>
</head>
<body>
  <div class="home">
    <h2>Rotate an Image</h2>
  </div>
  <script></script>
</body>
</html>

加载图片

添加用于选择文件的input元素,并使用按钮触发它。图片将被加载到两个img元素中,一个用于显示旋转的结果,另一个用于存储原始图像。

HTML:

html 复制代码
<button id="loadFileButton">Load a File</button>
<input style="display:none;" type="file" id="file" onchange="loadImageFromFile();" accept=".jpg,.jpeg,.png,.bmp" />
<div class="imageContainer">
  <img id="image"/>
  <img id="imageHidden"/>
</div>
<style>
  .imageContainer {
    max-width: 50%;
  }

  #image {
    max-width: 100%;
  }

  #imageHidden {
    display: none;
  }
</style>

JavaScript:

js 复制代码
function loadImageFromFile(){
  let fileInput = document.getElementById("file");
  let files = fileInput.files;
  if (files.length == 0) {
    return;
  }
  let file = files[0];
  fileReader = new FileReader();
  fileReader.onload = function(e){
    document.getElementById("image").src = e.target.result;
    document.getElementById("imageHidden").src = e.target.result;
  };
  fileReader.onerror = function () {
    console.warn('oops, something went wrong.');
  };
  fileReader.readAsDataURL(file);
}

使用CSS旋转图像

我们可以使用CSS属性transform来旋转HTML元素,用法如下:

js 复制代码
element.style.transform = 'rotate(45deg)';

我们还可以指定transform-origin这个属性来设置旋转变换的原点。默认情况下,变换的原点是图像中心。

下面是页面中的具体实现。

  1. 添加用于指定旋转角度的input元素。

    HTML:

    html 复制代码
    <label for="degree">Degree (0-360):</label>
    <input type="number" id="degree" name="degree" min="0" max="360" value="0"/>
  2. 监听input元素的change事件,用CSS旋转图像。

    js 复制代码
    document.getElementById("degree").addEventListener("change",function(e){
      const degree = e.target.value;
      rotateImage(degree);
    })
    
    function rotateImage(degree){
      const image = document.getElementById("image");
      const imageHidden = document.getElementById("imageHidden");
      if (!image.src) {
        return; //no image selected
      }
      image.src = imageHidden.src;
      image.style.transform = 'rotate(' + degree + 'deg)';
    }

使用CSS不会实际上改变图像数据,因此处理速度很快。但是它可能存在overflow的问题,会遮盖住其他元素。我们可以将其容器的overflow CSS属性设置为auto,以避免遮住其他元素,但图像将被剪切。

使用Canvas旋转图像

HTML5提供了一个canvas标签,我们可以用它操作图像数据。我们可以使用它来获得实际旋转后的图像。

  1. 向页面添加一个隐藏的canvas元素。

    html 复制代码
    <canvas id="canvasHidden"></canvas>
    <style>
      #canvasHidden {
        display: none;
      }
    </style>
  2. 添加一个select元素以选择要使用的旋转方法。

    html 复制代码
    <label>
      Method:
      <select id="methodSelect">
        <option>CSS</option>
        <option>Canvas</option>
      </select>
    </label>
  3. 当所选方法为"Canvas"时,使用canvas旋转图像。

    js 复制代码
    if (method == 0) {
      image.src = imageHidden.src;
      image.style.transform = 'rotate(' + degree + 'deg)';
    }else if (method == 1){
      image.style.transform = '';
      rotateImageWithCanvas(degree);
    }

下面我们讨论具体如何使用canvas旋转图像。

  1. 获取新的旋转图像的大小。

    旋转后,图像将具有新的大小。我们需要设置canvas的大小以匹配新的图像的大小。假设我们需要围绕图像的中心旋转图像,我们可以使用以下函数来计算新的大小。

    首先,得到四个角点,并计算它们在旋转后的位置。然后,基于这些点获得正外接矩形。

    js 复制代码
    function getBoundingRect(width,height,degree) {
      let rad = Degree2Rad(degree);
      let points = [{x:0,y:0},{x:width,y:0},{x:width,y:height},{x:0,y:height}];
      let minX = undefined;
      let minY = undefined;
      let maxX = 0;
      let maxY = 0;
      for (let index = 0; index < points.length; index++) {
        const point = points[index];
        const rotatedPoint = getRotatedPoint(point.x,point.y,width/2,height/2,rad);
        if (minX == undefined) {
          minX = rotatedPoint.x;
        }else{
          minX = Math.min(rotatedPoint.x,minX);
        }
        if (minY == undefined) {
          minY = rotatedPoint.y;
        }else{
          minY = Math.min(rotatedPoint.y,minY);
        }
        maxX = Math.max(rotatedPoint.x,maxX);
        maxY = Math.max(rotatedPoint.y,maxY);
      }
      let rectWidth = maxX - minX;
      let rectHeight = maxY - minY;
      let rect = {
        x: minX,
        y: minY,
        width: rectWidth,
        height: rectHeight
      }
      return rect;
    }
    
    function Degree2Rad(degree){
      return degree*Math.PI/180
    }
    
    //https://gamedev.stackexchange.com/questions/86755/how-to-calculate-corner-positions-marks-of-a-rotated-tilted-rectangle
    function getRotatedPoint(x,y,cx,cy,theta){
      let tempX = x - cx;
      let tempY = y - cy;
    
      // now apply rotation
      let rotatedX = tempX*Math.cos(theta) - tempY*Math.sin(theta);
      let rotatedY = tempX*Math.sin(theta) + tempY*Math.cos(theta);
    
      // translate back
      x = rotatedX + cx;
      y = rotatedY + cy;
      let point = {x:x,y:y};
      return point;
    }
  2. 将canvas的大小设置为旋转图像的大小。

    js 复制代码
    const canvas = document.getElementById("canvasHidden");
    const imgWidth = imageHidden.naturalWidth;
    const imgHeight = imageHidden.naturalHeight;
    const rect = getBoundingRect(imgWidth,imgHeight,degree);
    canvas.width = rect.width;
    canvas.height = rect.height;
  3. 获取canvas的context以执行操作。

    js 复制代码
    const ctx = canvas.getContext("2d");
  4. 使用translate将新的(0,0)原点位置设置为canvas的中心。

    js 复制代码
    ctx.translate(canvas.width/2,canvas.height/2);
  5. 使用rotate设置变换矩阵。

    js 复制代码
    ctx.rotate(Degree2Rad(degree));
  6. 使用drawImage绘制图像内容。

    js 复制代码
    ctx.drawImage(imageHidden, -imgWidth/2, -imgHeight/2);

    我们需要指定在目标canvas中放置源图像左上角的x轴和y轴坐标。在这里,由于新原点是canvas的中心,我们需要使用-imgWidth/2-imgHeight/2

  7. 显示旋转后的图像。

    js 复制代码
    image.src = canvas.toDataURL();

使用Dynamic Web TWAIN旋转图像

Dynamic Web TWAIN是一个文档扫描SDK,可以在浏览器中扫描文档。它提供了各种图像处理方法。我们可以使用其RotateEx方法旋转图像。

使用它的优点是,它可以用于批量处理大量图像,因为处理是使用本地进程完成的。

以下是使用它的步骤:

  1. 在页面中引入Dynamic Web TWAIN。

    html 复制代码
    <script src="https://unpkg.com/dwt@18.4.2/dist/dynamsoft.webtwain.min.js"></script>
  2. 添加Dynamic Web TWAIN作为新的旋转方法。

    html 复制代码
    <label>
      Method:
      <select id="methodSelect">
        <option>CSS</option>
        <option>Canvas</option>
        <option>Dynamic Web TWAIN</option>
      </select>
    </label>
  3. 当选择它作为旋转方法时,初始化一个Web TWAIN的实例并使用它来旋转图像。可以在此处申请其许可证。

    js 复制代码
    let DWObject;
    Dynamsoft.DWT.AutoLoad = false;
    Dynamsoft.DWT.ProductKey = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="; //one-day trial
    Dynamsoft.DWT.ResourcesPath = "https://unpkg.com/dwt@18.4.2/dist";
    async function rotateImage(degree){
      const method = document.getElementById("methodSelect").selectedIndex;
      const image = document.getElementById("image");
      const imageHidden = document.getElementById("imageHidden");
      if (!image.src) {
        return;
      }
      if (method == 0) {
        image.src = imageHidden.src;
        image.style.transform = 'rotate(' + degree + 'deg)';
      }else if (method == 1){
        image.style.transform = '';
        rotateImageWithCanvas(degree);
      }else if (method == 2){
        image.style.transform = '';
        if (!DWObject) {
          await init();
        }
        rotateImageWithDWT(degree);
      }
    }
    function init(){
      return new Promise((resolve, reject) => {
        const title = document.querySelector("h2").innerText;
        document.querySelector("h2").innerText = "Loading Dynamic Web TWAIN...";
        Dynamsoft.DWT.CreateDWTObjectEx(
        {
          WebTwainId: 'dwtcontrol'
        },
        function(obj) {
          DWObject = obj;
          document.querySelector("h2").innerText = title;
          resolve();
        },
        function(err) {
          console.log(err);
          document.querySelector("h2").innerText = "Failed to load Dynamic Web TWAIN";
          reject(err);
        }
      );
      })
    }
    
    function rotateImageWithDWT(degree){
      return new Promise(async (resolve, reject) => {
        if (DWObject) {
          DWObject.RemoveAllImages();
          let file = document.getElementById("file").files[0];
          let buffer = await file.arrayBuffer();
          DWObject.LoadImageFromBinary(buffer,
          function(){
            const method = document.getElementById("interpolationMethodSelect").selectedOptions[0].value;
            DWObject.RotateEx(0,degree,false,method,
            function(){
              document.getElementById("image").src = DWObject.GetImageURL(0);
            },
            function(errorCode, errorString){
              reject(errorString);
            });
          },
          function(errorCode, errorString){
            reject(errorString);
          })
    
        }else{
          reject();
        }
      })
    }
  4. 在旋转过程中需要添加新的像素,称为插值。Dynamic Web TWAIN为此提供了几种算法。

    可以添加一个select元素用于选择使用哪种算法。

    html 复制代码
    <div class="dwtcontrols">
      <label>
        Interpolation Method:
        <select id="interpolationMethodSelect">
          <option value="1">Nearest Neighbour</option>
          <option value="2">Bilinear</option>
          <option value="3">Bicubic</option>
          <option value="5" selected>Best Quality</option>
        </select>
      </label>
    </div>
  5. Web TWAIN还具有检测文档图像倾斜角度的功能。我们可以用它来确定需要的旋转角度。

    js 复制代码
    DWObject.GetSkewAngle(
      0,
      function(angle) {
        document.getElementById("degree").value = 360+angle;
        rotateImage(360+angle);
      },
      function(errorCode, errorString) {
        console.log(errorString);
      }
    );

源代码

可以在以下仓库中找到所有代码:

github.com/tony-xlh/Ro...

相关推荐
傻小胖1 分钟前
React 生命周期完整指南
前端·react.js
梦境之冢40 分钟前
axios 常见的content-type、responseType有哪些?
前端·javascript·http
racerun43 分钟前
vue VueResource & axios
前端·javascript·vue.js
J总裁的小芒果1 小时前
THREE.js 入门(六) 纹理、uv坐标
开发语言·javascript·uv
m0_548514771 小时前
前端Pako.js 压缩解压库 与 Java 的 zlib 压缩与解压 的互通实现
java·前端·javascript
AndrewPerfect1 小时前
xss csrf怎么预防?
前端·xss·csrf
Calm5501 小时前
Vue3:uv-upload图片上传
前端·vue.js
浮游本尊1 小时前
Nginx配置:如何在一个域名下运行两个网站
前端·javascript
m0_748239831 小时前
前端bug调试
前端·bug
m0_748232921 小时前
[项目][boost搜索引擎#4] cpp-httplib使用 log.hpp 前端 测试及总结
前端·搜索引擎