CSS 中操作移动,缩放和旋转

基于贴纸的功能:

  • 对贴纸进行移动,缩放和旋转的操作

在之前的文章中,我们讲解了 H5 Canvas 中操作移动,缩放和旋转

但是,如果贴纸过多的话,我们在一个 Canvas 中绘制,会出现多余的绘制问题。那么,解决这个问题,我们可以有一些思路来优化:

  1. 使用多个 Canvas。是可以解决多次绘制的问题,但是,缩放或者旋转可能会改变贴纸在 canvas 的展示,比如溢出的部分绘制不出来。
  2. 我们可以使用 css 来实现。通过 transform 属性。为什么不直接改变 WIDTH & HEIGHTTOP & LEFT 等属性呢?因为 transform 性能更好。

下面,我们来实现下标题中的功能👇

这里,我们使用 React 来实现下,相关的 HTML 结构如下:

typescript 复制代码
{
  stickerData.map((item, index) => {
    return (
      <div
        key={index}
        style={{
          width: item.size.width + "px",
          height: item.size.height + "px",
          zIndex: index,
        }}
        ref={(ref) => {
          item.stickerElement = ref;
        }}
        onMouseDown{(e) => {
          handleMouseDown(e, item);
        }}
        onMouseMove{(e) => {
          handleMove(e, item);
        }}
        onMouseUp={handleMoveEnd}
        onMouseLeave{handleMoveEnd}
        onWheel={(e) => {
          // 滚轮的滚动模拟缩放
          const scale = 0.1;
          handleZoom(1 + (e.deltaY > 0 ? -1 : 1) * scale, item);
        }}
      >
        Sticker
      </div>
    );
  });
}

解决方案

当鼠标点击的时候,我们记录下相关的坐标。

typescript 复制代码
const handleMouseDown = (event: { clientX: number, clientY: number }, sticker: IStickerData) => {
  state.isMoveLock = false;
  state.startClientX = event.clientX;
  state.startClientY = event.clientY;
  
  state.modelX = sticker.location.x; // 记录相关的偏移量 x
  state.modelY = sticker.location.y; // 记录相关的偏移量 y
  
  // 开始处理偏移函数,这个后面说
}

结束移动的时候,我们得把相关的标识符调整状态。

typescript 复制代码
const handleMoveEnd = React.useCallback(() => {
  state.isMoveLocak = true; // 锁定移动
  state.initDistance = -1; // 移动的距离,用来监听手势的移动的距离
})

我们使用鼠标滚轮进行缩放:

typescript 复制代码
const handleZoom = React.useCallback((scale: number, sticker: IStickerData) => {
  const width = sticker.size.width * state.initScale * scale;
  
  const stickerScale = width / sticker.size.widht;
  
  sticker.scale = stickerScale;
  
  // 开始处理偏移函数
  handleEachStickerElement(sticker);
})

重点是处理的偏移的函数 👇

typescript 复制代码
const handleEachStickerElement  = (sticker: IStickerData) => {
  // 核心
  sticker.$stickerElement.style.transform = `translate(${sticker.location.x}px, ${sticker.location.y}px) scale(${sticker.scale}) rotate(${sticker.rotate}deg)`
}

细心的读者会发现,我们这里并没有 rotate 的触发操作,我们可以添加额外的配置,比如在页面的固定位置触发旋转操作

后话

我们可以对贴纸的移动,缩放和旋转做以下的优化:

  1. 我们都是基于鼠标的控制,如果我们需要在触屏设备上处理的话。我们可以添加下面的方法👇
typescript 复制代码
<div
  onTouchStart={(e) => {
    const touchList: React.Touch[] = []; // 获取触发的列表
    // other
    handleMouseDown(touchList[touchList.length - 1], item);
  }}
  onTouchMove{(e) => {
    const touchList: React.Touch[] = [];
    // other
    if (touchList.length === 1 || touchList.length > 2) {
        handleMove(touchList[touchList.length - 1], item);
    } else if (touchList.length === 2) {
        if (state.initDistance < 0) {
            state.initDistance = twoTouchDistance(touchList); // 计算两个点的距离
        } else {
            const distance = twoTouchDistance(touchList);
            handleZoom(distance / state.initDistance, item); // 缩放功能
        }
    }
  }}
  onTouchEnd={handleMoveEnd}
>
  Sticker
</div>
  1. 我们可以对贴纸的移动范围进行限制。我们可以在函数 handleEachStickerElement 上进行修改👇
typescript 复制代码
// 限制贴纸范围
const limitStickerMoveRatioEtc = {
    scale: { min: 0.5, max: 2 },
    // other
};

type limitStickerMoveRatioEtcType = typeof limitStickerMoveRatioEtc;

const handleEachStickerElement = (sticker: IStickerData, limitRatioEtc: limitStickerMoveRatioEtcType = limitStickerMoveRatioEtc) {
   // 1. 如果不存在贴纸的元素,则进行处理,返回不操作并进行日志记录
   // 2. 限定移动和缩放的范围,上面的 limitStickerMoveRatioEtc 允许贴纸缩小到原来的一半,只能放大到原来的两倍
   // 3. 缓存贴纸对应的数据(缩放,移动和旋转)
}

参考

相关推荐
陈不知代码15 分钟前
uniapp创建vue3+ts+pinia+sass项目
前端·uni-app·sass
小王码农记17 分钟前
sass中@mixin与 @include
前端·sass
陈琦鹏24 分钟前
轻松管理 WebSocket 连接!easy-websocket-client
前端·vue.js·websocket
hui函数1 小时前
掌握JavaScript函数封装与作用域
前端·javascript
行板Andante1 小时前
前端设计中如何在鼠标悬浮时同步修改块内样式
前端
Carlos_sam2 小时前
Opnelayers:ol-wind之Field 类属性和方法详解
前端·javascript
小毛驴8502 小时前
创建 Vue 项目的 4 种主流方式
前端·javascript·vue.js
誰能久伴不乏2 小时前
Linux如何执行系统调用及高效执行系统调用:深入浅出的解析
java·服务器·前端
涔溪3 小时前
响应式前端设计:CSS 自适应布局与字体大小的最佳实践
前端·css
今禾3 小时前
前端开发中的Mock技术:深入理解vite-plugin-mock
前端·react.js·vite