图片边缘采样

时间:2025-04-06 08:10:53

普通采样

 const getEdgeColors = (img) => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.width = img.width;
    canvas.height = img.height;
    try {
      context.drawImage(img, 0, 0);

    // 获取左边缘颜色
      const leftPixel = context.getImageData(0, Math.floor(img.height / 2), 1, 1).data;
      const leftColor = `rgb(${leftPixel[0]}, ${leftPixel[1]}, ${leftPixel[2]})`;

    // 获取右边缘颜色
      const rightPixel = context.getImageData(img.width - 1, Math.floor(img.height / 2), 1, 1).data;
      const rightColor = `rgb(${rightPixel[0]}, ${rightPixel[1]}, ${rightPixel[2]})`;
      console.log('获取图片边缘颜色成功:', leftColor, rightColor);
      return { leftColor, rightColor };
    } catch (error) {
      console.error('获取图片边缘颜色失败:', error);
      return { leftColor: '#ffffff', rightColor: '#ffffff' };
    }
  };

优化:

1)采样多个点而不是只取中间一点,提高颜色准确性;
2)实现边缘区域的平均采样,避免单点采样可能带来的颜色偏差;
3)添加颜色增强处理,使背景色更加美观;
4)优化错误处理机制,确保在各种情况下都能返回合适的颜色值;
5)添加颜色平滑过渡效果,使轮播切换时背景色变化更加自然。

  // 获取图片边缘颜色 - 优化版本
  const getEdgeColors = (img) => {
    const canvas = document.createElement('canvas');
    const context = canvas.getContext('2d');
    canvas.width = img.width;
    canvas.height = img.height;
    
    try {
      context.drawImage(img, 0, 0);
      
      // 采样点数量和区域大小
      const samplePoints = 5; // 垂直方向采样点数量
      const sampleWidth = 10;  // 水平方向采样宽度
      const sampleHeight = Math.floor(img.height / samplePoints); // 每个采样点的高度间隔
      
      // 左边缘颜色采样
      let leftR = 0, leftG = 0, leftB = 0;
      for (let i = 0; i < samplePoints; i++) {
        // 计算当前采样点的垂直位置
        const y = Math.floor(sampleHeight * (i + 0.5));
        
        // 获取左边缘区域的平均颜色
        const leftData = context.getImageData(0, y, sampleWidth, 1).data;
        for (let j = 0; j < sampleWidth * 4; j += 4) {
          leftR += leftData[j];
          leftG += leftData[j + 1];
          leftB += leftData[j + 2];
        }
      }
      
      // 计算左边缘平均颜色
      leftR = Math.floor(leftR / (samplePoints * sampleWidth));
      leftG = Math.floor(leftG / (samplePoints * sampleWidth));
      leftB = Math.floor(leftB / (samplePoints * sampleWidth));
      
      // 右边缘颜色采样
      let rightR = 0, rightG = 0, rightB = 0;
      for (let i = 0; i < samplePoints; i++) {
        // 计算当前采样点的垂直位置
        const y = Math.floor(sampleHeight * (i + 0.5));
        
        // 获取右边缘区域的平均颜色
        const rightData = context.getImageData(img.width - sampleWidth, y, sampleWidth, 1).data;
        for (let j = 0; j < sampleWidth * 4; j += 4) {
          rightR += rightData[j];
          rightG += rightData[j + 1];
          rightB += rightData[j + 2];
        }
      }
      
      // 计算右边缘平均颜色
      rightR = Math.floor(rightR / (samplePoints * sampleWidth));
      rightG = Math.floor(rightG / (samplePoints * sampleWidth));
      rightB = Math.floor(rightB / (samplePoints * sampleWidth));
      
      // 颜色增强处理 - 适当提高饱和度
      const enhanceColor = (r, g, b) => {
        // 计算亮度
        const brightness = (r + g + b) / 3;
        
        // 如果颜色太暗或太亮,适当调整
        if (brightness < 30) {
          // 提亮暗色
          return {
            r: Math.min(255, r + 30),
            g: Math.min(255, g + 30),
            b: Math.min(255, b + 30)
          };
        } else if (brightness > 220) {
          // 降低过亮的颜色
          return {
            r: Math.max(0, r - 20),
            g: Math.max(0, g - 20),
            b: Math.max(0, b - 20)
          };
        } else {
          // 适当提高饱和度
          const max = Math.max(r, g, b);
          const min = Math.min(r, g, b);
          const delta = max - min;
          
          if (delta > 10) { // 有一定色差才增强饱和度
            const saturationFactor = 1.2;
            const newR = r + (r - brightness) * saturationFactor;
            const newG = g + (g - brightness) * saturationFactor;
            const newB = b + (b - brightness) * saturationFactor;
            
            return {
              r: Math.min(255, Math.max(0, Math.round(newR))),
              g: Math.min(255, Math.max(0, Math.round(newG))),
              b: Math.min(255, Math.max(0, Math.round(newB)))
            };
          }
          
          return { r, g, b };
        }
      };
      
      // 应用颜色增强
      const enhancedLeft = enhanceColor(leftR, leftG, leftB);
      const enhancedRight = enhanceColor(rightR, rightG, rightB);
      
      // 生成最终颜色
      const leftColor = `rgb(${enhancedLeft.r}, ${enhancedLeft.g}, ${enhancedLeft.b})`;
      const rightColor = `rgb(${enhancedRight.r}, ${enhancedRight.g}, ${enhancedRight.b})`;
      
      console.log('获取图片边缘颜色成功:', leftColor, rightColor);
      return { leftColor, rightColor };
    } catch (error) {
      console.error('获取图片边缘颜色失败:', error);
      // 返回一个柔和的默认颜色,而不是纯白色
      return { leftColor: '#f5f5f5', rightColor: '#f5f5f5' };
    }
  };

采用切片的方案进行循环填充

// 创建边缘切片的canvas
export const createEdgeSlice = (img: HTMLImageElement, isLeft: boolean): string => {
  const canvas = document.createElement('canvas');
  const ctx = canvas.getContext('2d');
  const sliceWidth = 1; // 切片宽度

  canvas.width = sliceWidth;
  canvas.height = img.height;

  if (isLeft) {
    if (ctx) {
      ctx.drawImage(img, 0, 0, sliceWidth, img.height, 0, 0, sliceWidth, img.height);
    } else {
      // 如果ctx为null则抛出错误
      throw new Error('无法获取canvas上下文');
    }
  } else {
    if (ctx) {
      ctx.drawImage(img, img.width - sliceWidth, 0, sliceWidth, img.height, 0, 0, sliceWidth, img.height);
    } else {
      // 如果ctx为null则抛出错误
      throw new Error('无法获取canvas上下文');
    }
  }
  console.log('获取到了边缘切片 base64')
  return canvas.toDataURL();
};

采样某个点进行渐变

export const getEdgeColors = (img) => {
  const canvas = document.createElement('canvas');
  const context = canvas.getContext('2d');
  canvas.width = img.width;
  canvas.height = img.height;
  try {
    // 检查 context 是否为 null
    if (!context) {
      throw new Error('无法获取 canvas context');
    }
    context.drawImage(img, 0, 0);

    // 获取顶部中间位置的左右边缘颜色
    const middleX = Math.floor(img.width / 2);
    // 使用中间点位即可
    const middlePixel = context.getImageData(middleX, 0, 1, 1).data;
    // const leftPixel = context.getImageData(0, 0, 1, 1).data;
    // const rightPixel = context.getImageData(img.width - 1, 0, 1, 1).data;
    const leftPixel = middlePixel;
    const rightPixel = middlePixel;
    // 创建渐变色字符串
    const createGradient = (color, direction) => {
      const [r, g, b] = color;
      // direction: 渐变方向,可以是'right'或'left'
      // r,g,b: RGB颜色值
      // 渐变包含6个颜色节点:
      // 1. 0%位置: 完全不透明的原始颜色
      // 2. 20%位置: 85%透明度
      // 3. 40%位置: 60%透明度
      // 4. 60%位置: 35%透明度
      // 5. 80%位置: 15%透明度
      // 6. 100%位置: 完全透明
      return `linear-gradient(to ${direction}, rgb(${r}, ${g}, ${b}) 0%, rgba(${r}, ${g}, ${b}, 0.85) 20%, rgba(${r}, ${g}, ${b}, 0.6) 40%, rgba(${r}, ${g}, ${b}, 0.35) 60%, rgba(${r}, ${g}, ${b}, 0.15) 80%, rgba(${r}, ${g}, ${b}, 0) 100%)`;
    };

    const leftColor = createGradient([leftPixel[0], leftPixel[1], leftPixel[2]], 'right');
    const rightColor = createGradient([rightPixel[0], rightPixel[1], rightPixel[2]], 'left');

    console.log('获取图片边缘渐变颜色成功:', leftColor, rightColor);
    return { leftColor, rightColor };
  } catch (error) {
    console.error('获取图片边缘颜色失败:', error);
    // 返回柔和的默认渐变颜色
    const defaultColor = [245, 245, 245]; // #f5f5f5
    return {
      leftColor: `linear-gradient(to right, rgb(${defaultColor.join(',')}) 0%, rgba(${defaultColor.join(',')}, 0) 100%)`,
      rightColor: `linear-gradient(to left, rgb(${defaultColor.join(',')}) 0%, rgba(${defaultColor.join(',')}, 0) 100%)`
    };
  }
};