前言

canvas有自带的 scale 方法和 rotate 方法可以实现缩放旋转,但是其作用极其简陋,要想方便调用需要自己封装。

1. 中心点旋转

思路很简单,先将画布移动到物体中心,然后旋转,再返回原处,再绘制物体即可,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>旋转</title>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="400" height="400"></canvas>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    let x = 100;
    let y = 100;
    let width = 100;
    let height = 100;
    // 绘制旋转的矩形
    function drawRectangle() {
      ctx.fillStyle = 'red'; // 设置填充颜色为红色
      ctx.fillRect(x, y, width, height); // 绘制矩形
    }

    /**
     * 中心点旋转
     * @param {CanvasRenderingContext2D} ctx canvas 2D 上下文
     * @param {Function} callback 绘制旋转矩形的回调函数
     * @param {Object} setting 旋转设置
     * @param {Number} setting.angle 旋转角度,弧度制
     */
     function rotateCenterPoint(ctx, setting, callback) {
      const { rectX, rectY, width, height, angle } = setting;
      ctx.save();
      ctx.translate(rectX + width / 2, rectY + height / 2); // 平移到 (100, 100)
      ctx.rotate(setting.angle); // 旋转 90 度
      ctx.translate(-(rectX + width / 2), -(rectY + height / 2)); // 平移回到原点
      callback(); // 绘制旋转矩形
      ctx.restore(); // 恢复原始状态
    }
    drawRectangle()
    rotateCenterPoint(ctx, { rectX: 100, rectY: 100, width: 100, height: 100, angle: 45 * Math.PI / 180 }, drawRectangle)
  </script>
</body>
</html>

image.png

2. 各个顶点缩放

思路和旋转一样,也是先平移,缩放,回到原处,绘制物体,不同的是需要根据模式判断是从什么点缩放的,随后需要平移补偿缩放的距离,具体代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>缩放</title>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="400" height="400"></canvas>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');

    let x = 100;
    let y = 100;
    let width = 100;
    let height = 100;
    // 绘制旋转的矩形
    function drawRectangle() {
      ctx.fillStyle = 'red'; // 设置填充颜色为红色
      ctx.fillRect(x, y, width, height); // 绘制矩形
    }

    /**
     * 缩放功能
     * callback 绘制旋转矩形的函数
     * mode 从什么地方缩放
     * setting.rectX 矩形 x 坐标
     * setting.rectY 矩形 y 坐标
     * setting.width 矩形宽度
     * setting.height 矩形高度
     * setting.scaleX 缩放比例 x
     * setting.scaleY 缩放比例 y
     */
     function scaleRect(ctx, setting, callback) {
      const { rectX, rectY, width, height, scaleX, scaleY, scaleMode } = setting;
      ctx.save();
      ctx.translate(rectX, rectY); // 平移到 (0, 0)
      ctx.scale(scaleX, scaleY);
      let translateX = 0;
      let translateY = 0;
      if (scaleMode === 'left-top') {
        // 左上角点固定的缩放
      } else if (scaleMode == 'right-top') {
        // 右上角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
      } else if (scaleMode == 'left-bottom') {
        // 左下角点固定的缩放
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      } else if (scaleMode == 'right-bottom') {
        // 右下角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      }
      ctx.translate(translateX, translateY);
      ctx.translate(-rectX, -rectY); // 平移回到原点
      callback();
      ctx.restore(); // 恢复原始状态
      return {
        rectX: rectX + translateX * scaleX,
        rectY: rectY + translateY * scaleY,
        width: width * scaleX,
        height: height * scaleY,
      }
    }

    // scaleRect(ctx, {
    //   rectX: x,
    //   rectY: y,
    //   width: width,
    //   height: height,
    //   scaleX: 1.5,
    //   scaleY: 1.5,
    //   scaleMode: 'left-top',
    // }, drawRectangle)
    scaleRect(ctx, {
      rectX: x,
      rectY: y,
      width: width,
      height: height,
      scaleX: 1.5,
      scaleY: 1.5,
      scaleMode: 'left-bottom',
    }, drawRectangle)

    ctx.fillStyle = 'yellow'; // 设置填充颜色为红色
    ctx.fillRect(100, 100, 100, 100); // 绘制矩形
  </script>
</body>
</html>

左下角缩放图:

image.png

3. 同时旋转缩放

先缩放,然后再将缩放得到的新坐标用于旋转即可。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>混合旋转缩放</title>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="400" height="400"></canvas>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    // 绘制旋转的矩形
    function drawRectangle(x, y, width, height, color = 'red') {
      ctx.fillStyle = color; // 设置填充颜色为红色
      ctx.fillRect(x, y, width, height); // 绘制矩形
    }

    /**
     * 中心点旋转
     * @param {CanvasRenderingContext2D} ctx canvas 2D 上下文
     * @param {Function} callback 绘制旋转矩形的回调函数
     * @param {Object} setting 旋转设置
     * @param {Number} setting.angle 旋转角度,弧度制
     */
     function rotateCenterPoint(ctx, setting, callback) {
      const { rectX, rectY, width, height, angle } = setting;
      ctx.save();
      ctx.translate(rectX + width / 2, rectY + height / 2); // 平移到 (100, 100)
      ctx.rotate(setting.angle); // 旋转 90 度
      ctx.translate(-(rectX + width / 2), -(rectY + height / 2)); // 平移回到原点
      callback(); // 绘制旋转矩形
      ctx.restore(); // 恢复原始状态
    }
    drawRectangle(100, 100, 100, 100, 'yellow')

    /**
     * 缩放功能
     * callback 绘制旋转矩形的函数
     * mode 从什么地方缩放
     * setting.rectX 矩形 x 坐标
     * setting.rectY 矩形 y 坐标
     * setting.width 矩形宽度
     * setting.height 矩形高度
     * setting.scaleX 缩放比例 x
     * setting.scaleY 缩放比例 y
     */
     function scaleRect(ctx, setting, callback) {
      const { rectX, rectY, width, height, scaleX, scaleY, scaleMode } = setting;
      ctx.save();
      ctx.translate(rectX, rectY); // 平移到 (0, 0)
      ctx.scale(scaleX, scaleY);
      let translateX = 0;
      let translateY = 0;
      if (scaleMode === 'left-top') {
        // 左上角点固定的缩放
      } else if (scaleMode == 'right-top') {
        // 右上角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
      } else if (scaleMode == 'left-bottom') {
        // 左下角点固定的缩放
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      } else if (scaleMode == 'right-bottom') {
        // 右下角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      }
      ctx.translate(translateX, translateY);
      ctx.translate(-rectX, -rectY); // 平移回到原点
      callback();
      ctx.restore(); // 恢复原始状态
      return {
        rectX: rectX + translateX * scaleX,
        rectY: rectY + translateY * scaleY,
        width: width * scaleX,
        height: height * scaleY,
      }
    }

    /** 
     * 混合旋转缩放
    */
    function mixinRotateAndScale(ctx, setting, callback) {
      const { rectX, rectY, width, height, angle = 0, scaleX, scaleY, scaleMode } = setting;
      // 先得到缩放后的新坐标
      const scaleInfo = scaleRect(ctx, { rectX, rectY, width, height, scaleX, scaleY, scaleMode }, callback);
      console.log(scaleInfo)
      // 再旋转并绘画
      rotateCenterPoint(ctx, { ...scaleInfo, angle }, 
        callback.bind(null, scaleInfo.rectX, scaleInfo.rectY, scaleInfo.width, scaleInfo.height)
      )
    }

    mixinRotateAndScale(ctx, { rectX: 100, rectY: 100, width: 100, height: 100, angle: 45 * Math.PI / 180, scaleX: 0.5, scaleY: 1.5, scaleMode: 'right-top' }, drawRectangle)
  </script>
</body>
</html>


image.png
theme: smartblue

前言

canvas有自带的 scale 方法和 rotate 方法可以实现缩放旋转,但是其作用极其简陋,要想方便调用需要自己封装。

1. 中心点旋转

思路很简单,先将画布移动到物体中心,然后旋转,再返回原处,再绘制物体即可,代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>旋转</title>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="400" height="400"></canvas>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    let x = 100;
    let y = 100;
    let width = 100;
    let height = 100;
    // 绘制旋转的矩形
    function drawRectangle() {
      ctx.fillStyle = 'red'; // 设置填充颜色为红色
      ctx.fillRect(x, y, width, height); // 绘制矩形
    }

    /**
     * 中心点旋转
     * @param {CanvasRenderingContext2D} ctx canvas 2D 上下文
     * @param {Function} callback 绘制旋转矩形的回调函数
     * @param {Object} setting 旋转设置
     * @param {Number} setting.angle 旋转角度,弧度制
     */
     function rotateCenterPoint(ctx, setting, callback) {
      const { rectX, rectY, width, height, angle } = setting;
      ctx.save();
      ctx.translate(rectX + width / 2, rectY + height / 2); // 平移到 (100, 100)
      ctx.rotate(setting.angle); // 旋转 90 度
      ctx.translate(-(rectX + width / 2), -(rectY + height / 2)); // 平移回到原点
      callback(); // 绘制旋转矩形
      ctx.restore(); // 恢复原始状态
    }
    drawRectangle()
    rotateCenterPoint(ctx, { rectX: 100, rectY: 100, width: 100, height: 100, angle: 45 * Math.PI / 180 }, drawRectangle)
  </script>
</body>
</html>

image.png

2. 各个顶点缩放

思路和旋转一样,也是先平移,缩放,回到原处,绘制物体,不同的是需要根据模式判断是从什么点缩放的,随后需要平移补偿缩放的距离,具体代码如下:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>缩放</title>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="400" height="400"></canvas>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');

    let x = 100;
    let y = 100;
    let width = 100;
    let height = 100;
    // 绘制旋转的矩形
    function drawRectangle() {
      ctx.fillStyle = 'red'; // 设置填充颜色为红色
      ctx.fillRect(x, y, width, height); // 绘制矩形
    }

    /**
     * 缩放功能
     * callback 绘制旋转矩形的函数
     * mode 从什么地方缩放
     * setting.rectX 矩形 x 坐标
     * setting.rectY 矩形 y 坐标
     * setting.width 矩形宽度
     * setting.height 矩形高度
     * setting.scaleX 缩放比例 x
     * setting.scaleY 缩放比例 y
     */
     function scaleRect(ctx, setting, callback) {
      const { rectX, rectY, width, height, scaleX, scaleY, scaleMode } = setting;
      ctx.save();
      ctx.translate(rectX, rectY); // 平移到 (0, 0)
      ctx.scale(scaleX, scaleY);
      let translateX = 0;
      let translateY = 0;
      if (scaleMode === 'left-top') {
        // 左上角点固定的缩放
      } else if (scaleMode == 'right-top') {
        // 右上角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
      } else if (scaleMode == 'left-bottom') {
        // 左下角点固定的缩放
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      } else if (scaleMode == 'right-bottom') {
        // 右下角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      }
      ctx.translate(translateX, translateY);
      ctx.translate(-rectX, -rectY); // 平移回到原点
      callback();
      ctx.restore(); // 恢复原始状态
      return {
        rectX: rectX + translateX * scaleX,
        rectY: rectY + translateY * scaleY,
        width: width * scaleX,
        height: height * scaleY,
      }
    }

    // scaleRect(ctx, {
    //   rectX: x,
    //   rectY: y,
    //   width: width,
    //   height: height,
    //   scaleX: 1.5,
    //   scaleY: 1.5,
    //   scaleMode: 'left-top',
    // }, drawRectangle)
    scaleRect(ctx, {
      rectX: x,
      rectY: y,
      width: width,
      height: height,
      scaleX: 1.5,
      scaleY: 1.5,
      scaleMode: 'left-bottom',
    }, drawRectangle)

    ctx.fillStyle = 'yellow'; // 设置填充颜色为红色
    ctx.fillRect(100, 100, 100, 100); // 绘制矩形
  </script>
</body>
</html>

左下角缩放图:

image.png

3. 同时旋转缩放

先缩放,然后再将缩放得到的新坐标用于旋转即可。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>混合旋转缩放</title>
  <style>
    #myCanvas {
      border: 1px solid black;
    }
  </style>
</head>
<body>
  <canvas id="myCanvas" width="400" height="400"></canvas>
  <script>
    const canvas = document.getElementById('myCanvas');
    const ctx = canvas.getContext('2d');
    // 绘制旋转的矩形
    function drawRectangle(x, y, width, height, color = 'red') {
      ctx.fillStyle = color; // 设置填充颜色为红色
      ctx.fillRect(x, y, width, height); // 绘制矩形
    }

    /**
     * 中心点旋转
     * @param {CanvasRenderingContext2D} ctx canvas 2D 上下文
     * @param {Function} callback 绘制旋转矩形的回调函数
     * @param {Object} setting 旋转设置
     * @param {Number} setting.angle 旋转角度,弧度制
     */
     function rotateCenterPoint(ctx, setting, callback) {
      const { rectX, rectY, width, height, angle } = setting;
      ctx.save();
      ctx.translate(rectX + width / 2, rectY + height / 2); // 平移到 (100, 100)
      ctx.rotate(setting.angle); // 旋转 90 度
      ctx.translate(-(rectX + width / 2), -(rectY + height / 2)); // 平移回到原点
      callback(); // 绘制旋转矩形
      ctx.restore(); // 恢复原始状态
    }
    drawRectangle(100, 100, 100, 100, 'yellow')

    /**
     * 缩放功能
     * callback 绘制旋转矩形的函数
     * mode 从什么地方缩放
     * setting.rectX 矩形 x 坐标
     * setting.rectY 矩形 y 坐标
     * setting.width 矩形宽度
     * setting.height 矩形高度
     * setting.scaleX 缩放比例 x
     * setting.scaleY 缩放比例 y
     */
     function scaleRect(ctx, setting, callback) {
      const { rectX, rectY, width, height, scaleX, scaleY, scaleMode } = setting;
      ctx.save();
      ctx.translate(rectX, rectY); // 平移到 (0, 0)
      ctx.scale(scaleX, scaleY);
      let translateX = 0;
      let translateY = 0;
      if (scaleMode === 'left-top') {
        // 左上角点固定的缩放
      } else if (scaleMode == 'right-top') {
        // 右上角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
      } else if (scaleMode == 'left-bottom') {
        // 左下角点固定的缩放
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      } else if (scaleMode == 'right-bottom') {
        // 右下角点固定的缩放
        translateX = (width - width * scaleX) / scaleX; // 补偿偏移量
        translateY = (height - height * scaleY) / scaleY; // 补偿偏移量
      }
      ctx.translate(translateX, translateY);
      ctx.translate(-rectX, -rectY); // 平移回到原点
      callback();
      ctx.restore(); // 恢复原始状态
      return {
        rectX: rectX + translateX * scaleX,
        rectY: rectY + translateY * scaleY,
        width: width * scaleX,
        height: height * scaleY,
      }
    }

    /** 
     * 混合旋转缩放
    */
    function mixinRotateAndScale(ctx, setting, callback) {
      const { rectX, rectY, width, height, angle = 0, scaleX, scaleY, scaleMode } = setting;
      // 先得到缩放后的新坐标
      const scaleInfo = scaleRect(ctx, { rectX, rectY, width, height, scaleX, scaleY, scaleMode }, callback);
      console.log(scaleInfo)
      // 再旋转并绘画
      rotateCenterPoint(ctx, { ...scaleInfo, angle }, 
        callback.bind(null, scaleInfo.rectX, scaleInfo.rectY, scaleInfo.width, scaleInfo.height)
      )
    }

    mixinRotateAndScale(ctx, { rectX: 100, rectY: 100, width: 100, height: 100, angle: 45 * Math.PI / 180, scaleX: 0.5, scaleY: 1.5, scaleMode: 'right-top' }, drawRectangle)
  </script>
</body>
</html>


image.png

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐