Skip to content

实际应用案例

本节介绍 tui-entine 在实际项目中的应用案例,展示其在不同场景下的使用方式。

t-color 组件

t-color 是一个颜色选择器组件,使用 tui-entine 实现了以下功能:

核心功能

  • 颜色选择区域:使用 Canvas 绘制颜色选择面板,支持 HSL 颜色模型
  • 透明度滑条:可调节颜色透明度的滑动条
  • 拖拽手柄:用于在颜色面板上选择颜色
  • 颜色块预览:实时显示当前选中的颜色
  • 文本区域:显示颜色值(HEX、RGB、HSL 等格式)

技术实现

ts
// 颜色选择区域绘制
function drawColorPanel(engine: ITuiEngine, hue: number) {
  const panel = engine.createView();
  panel.style.width = 200;
  panel.style.height = 200;
  panel.style.backgroundColor = `hsl(${hue}, 100%, 50%)`;

  // 绘制黑白渐变遮罩
  const overlay = engine.createView();
  overlay.style.width = 200;
  overlay.style.height = 200;
  overlay.style.backgroundImage =
    "linear-gradient(to right, white, transparent), linear-gradient(to top, black, transparent)";

  panel.appendChild(overlay);
  return panel;
}

// 拖拽手柄
function createColorHandle(engine: ITuiEngine) {
  const handle = engine.createView();
  handle.style.width = 20;
  handle.style.height = 20;
  handle.style.borderWidth = 2;
  handle.style.borderColor = "#ffffff";
  handle.style.borderRadius = 10;
  handle.style.position = "absolute";

  // 添加拖拽事件
  let isDragging = false;
  handle.addEventListener("touchstart", () => (isDragging = true));
  handle.addEventListener("touchmove", (event) => {
    if (isDragging) {
      // 更新手柄位置
      handle.style.left = event.clientX - 10;
      handle.style.top = event.clientY - 10;
      engine.updateElement(handle, true);

      // 计算新的颜色值
      updateColorFromPosition(event.clientX, event.clientY);
    }
  });
  handle.addEventListener("touchend", () => (isDragging = false));

  return handle;
}

性能优化

  • 局部更新:只更新颜色选择手柄和颜色预览,不重绘整个面板
  • 事件节流:对 touchmove 事件进行节流,减少计算次数
  • 缓存:缓存颜色面板的静态部分,只更新动态元素

t-joystick 组件

t-joystick 是一个虚拟摇杆组件,使用 tui-entine 实现了以下功能:

核心功能

  • 摇杆底盘:绘制圆形摇杆底座
  • 摇杆控制:可拖动的摇杆控制杆
  • 箭头图片:指示摇杆方向
  • 指南针图片:显示当前方向
  • 小球图片:作为摇杆的控制点

技术实现

ts
// 摇杆底盘
function createJoystickBase(engine: ITuiEngine) {
  const base = engine.createView();
  base.style.width = 120;
  base.style.height = 120;
  base.style.backgroundColor = "rgba(0, 0, 0, 0.3)";
  base.style.borderRadius = 60;
  base.style.justifyContent = "center";
  base.style.alignItems = "center";

  return base;
}

// 摇杆控制杆
function createJoystickStick(engine: ITuiEngine) {
  const stick = engine.createView();
  stick.style.width = 60;
  stick.style.height = 60;
  stick.style.backgroundColor = "rgba(255, 255, 255, 0.8)";
  stick.style.borderRadius = 30;

  // 添加拖拽事件
  let isDragging = false;
  let startX = 0;
  let startY = 0;

  stick.addEventListener("touchstart", (event) => {
    isDragging = true;
    startX = event.clientX;
    startY = event.clientY;
  });

  stick.addEventListener("touchmove", (event) => {
    if (isDragging) {
      const deltaX = event.clientX - startX;
      const deltaY = event.clientY - startY;

      // 限制在圆形范围内
      const distance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
      const maxDistance = 30; // 摇杆半径

      if (distance > maxDistance) {
        const angle = Math.atan2(deltaY, deltaX);
        stick.style.transform = {
          translateX: Math.cos(angle) * maxDistance,
          translateY: Math.sin(angle) * maxDistance,
        };
      } else {
        stick.style.transform = {
          translateX: deltaX,
          translateY: deltaY,
        };
      }

      engine.updateElement(stick, true);

      // 计算方向和强度
      updateJoystickDirection(deltaX, deltaY);
    }
  });

  stick.addEventListener("touchend", () => {
    isDragging = false;
    // 重置位置
    stick.style.transform = {
      translateX: 0,
      translateY: 0,
    };
    engine.updateElement(stick, true);
  });

  return stick;
}

性能优化

  • 局部更新:只更新摇杆位置,不重绘整个组件
  • 事件处理:使用触摸事件而非鼠标事件,提高响应速度
  • 数学优化:使用三角函数计算摇杆位置,减少计算量

t-slider-menu 组件

t-slider-menu 是一个侧滑菜单组件,使用 tui-entine 实现了以下功能:

核心功能

  • 自定义左侧菜单:可滑动的左侧菜单
  • 自定义右侧内容区:主内容区域
  • 滚动裁剪:按当前滚动位置裁剪可见项
  • 惯性滚动:支持手势惯性滚动
  • 边界回弹:到达边界时的回弹效果

技术实现

ts
// 菜单容器
function createMenuContainer(engine: ITuiEngine) {
  const container = engine.createView();
  container.style.width = 250;
  container.style.height = engine.height;
  container.style.backgroundColor = "#ffffff";
  container.style.position = "absolute";
  container.style.left = -250; // 默认隐藏

  // 菜单项
  const menuItems = [];
  for (let i = 0; i < 20; i++) {
    const item = engine.createView();
    item.style.width = "100%";
    item.style.height = 50;
    item.style.paddingLeft = 20;
    item.style.justifyContent = "center";
    item.style.alignItems = "flex-start";
    item.style.borderBottomWidth = 1;
    item.style.borderBottomColor = "#f0f0f0";

    const text = engine.createText();
    text.value = `菜单项 ${i + 1}`;
    text.style.fontSize = 16;
    text.style.color = "#333333";

    item.appendChild(text);
    menuItems.push(item);
  }

  container.appendChild(...menuItems);
  return container;
}

// 滑动处理
function handleMenuSwipe(engine: ITuiEngine, menu: any) {
  let startX = 0;
  let currentX = -250;
  let isDragging = false;

  menu.addEventListener("touchstart", (event: any) => {
    isDragging = true;
    startX = event.clientX;
  });

  menu.addEventListener("touchmove", (event: any) => {
    if (isDragging) {
      const deltaX = event.clientX - startX;
      currentX = Math.max(-250, Math.min(0, currentX + deltaX));
      menu.style.left = currentX;
      engine.updateElement(menu, true);
      startX = event.clientX;
    }
  });

  menu.addEventListener("touchend", () => {
    isDragging = false;
    // 自动回弹
    if (currentX > -125) {
      // 打开菜单
      animateTo(menu, 0);
    } else {
      // 关闭菜单
      animateTo(menu, -250);
    }
  });
}

// 动画函数
function animateTo(element: any, targetValue: number) {
  let currentValue = element.style.left;
  const duration = 300;
  const startTime = Date.now();

  function animate() {
    const elapsed = Date.now() - startTime;
    const progress = Math.min(elapsed / duration, 1);
    // 缓动函数
    const easeProgress = 1 - Math.pow(1 - progress, 3);

    element.style.left =
      currentValue + (targetValue - currentValue) * easeProgress;

    if (progress < 1) {
      requestAnimationFrame(animate);
    }
  }

  animate();
}

性能优化

  • 滚动裁剪:只绘制可见区域的菜单项,减少绘制量
  • 惯性滚动:使用物理模型模拟惯性,提高用户体验
  • 批量更新:使用 requestAnimationFrame 批量更新,减少重绘次数

其他应用案例

1. 自定义仪表盘

功能

  • 圆形仪表盘绘制
  • 指针动画效果
  • 实时数据更新

技术点

  • Canvas 路径绘制
  • 三角函数计算
  • 动画缓动效果

2. 绘图板

功能

  • 自由绘制
  • 颜色选择
  • 笔触大小调整

技术点

  • 触摸事件跟踪
  • 路径绘制
  • 局部更新优化

3. 粒子效果

功能

  • 粒子系统
  • 物理碰撞
  • 动画效果

技术点

  • 批量绘制
  • 物理引擎
  • 性能优化

总结

tui-entine 在以下场景中表现优异:

  1. 高频重绘场景:如颜色选择器、仪表盘等
  2. 复杂交互场景:如摇杆、绘图板等
  3. 自定义渲染场景:如侧滑菜单、粒子效果等
  4. 性能敏感场景:需要精确控制渲染范围的场景

通过合理使用 tui-entine 的 API,可以创建出性能优异、交互流畅的复杂组件,为用户提供更好的体验。

提示

  • 对于简单的静态布局,直接使用原生组件可能更合适
  • 对于需要精细控制渲染的场景,tui-entine 是理想选择
  • 合理使用局部更新和缓存,可以显著提高性能
  • 结合 t-canvas-engine 使用,可以更方便地管理 Canvas 生命周期