SolidJS-每日小知识(9/24)

对图片指定范围的区域进行填充显示

1 定义变量,svg和image

  // 用于保存SVG元素的引用
  const [svgRef, setSvgRef] = createSignal<SVGSVGElement | null>(null);

  // 图像原始尺寸
  const imageSize = { width: 11920, height: 16850 };
  // 裁剪区域
  const croppedScope = { x: 5000, y: 10000, width: 5000, height: 5000 };
  // SVG显示区域尺寸
  const svgSize = { width: 500, height: 400 };

  // 用于计算当前图像在SVG中的显示区域
  let imageInScopeX, imageInScopeY, imageInScopeW, imageInScopeH;
  // 用于定义裁剪区域
  let clipX, clipY, clipW, clipH;

  // 添加图像元素
  const addImageElement = (g) => {
    g.append('image')
      .attr('href', './src/assets/creatives/page_1_highres.jpg')
      .attr('x', imageInScopeX)
      .attr('y', imageInScopeY)
      .attr('width', imageInScopeW)
      .attr('height', imageInScopeH)
      .attr('clip-path', 'url(#clip)');
  };

  // 在组件挂载时初始化SVG和图像
  onMount(() => {
    const svg = d3.select(svgRef())
      .attr('width', svgSize.width)
      .attr('height', svgSize.height);
  
    // 创建外层g元素
    const outerGroup = svg.append('g').attr('id', 'outerGroup'); // 新增外层 g
  
    // 内部的 g 元素 (现有的 g)
    const innerGroup = outerGroup.append('g').attr('id', 'innerGroup'); // 设置 ID 为 innerGroup
  
    const scale = calculateScale();
    calculateImageAttributes(scale);
    calculateClipAttributes();
  
    addImageElement(innerGroup); // 修改为使用 innerGroup
  });

  // 渲染SVG和按钮
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <svg ref={setSvgRef} style={{ border: '1px solid black' }} />
    </div>
  );

2 计算缩放比例,显示区域范围

  // 计算缩放比例
  const calculateScale = () => {
    const imageW2H = imageSize.width / imageSize.height;
    const svgW2H = svgSize.width / svgSize.height;

    return imageW2H > svgW2H 
      ? svgSize.width / imageSize.width 
      : svgSize.height / imageSize.height;
  };

  // 根据缩放比例计算图像显示属性
  const calculateImageAttributes = (scale) => {
    imageInScopeW = imageSize.width * scale;
    imageInScopeH = imageSize.height * scale;
    imageInScopeX = (svgSize.width - imageInScopeW) / 2;
    imageInScopeY = (svgSize.height - imageInScopeH) / 2;
  };

  // 计算裁剪区域属性
  const calculateClipAttributes = () => {
    clipX = imageInScopeX + (croppedScope.x / imageSize.width) * imageInScopeW;
    clipY = imageInScopeY + (croppedScope.y / imageSize.height) * imageInScopeH;
    clipW = (croppedScope.width / imageSize.width) * imageInScopeW;
    clipH = (croppedScope.height / imageSize.height) * imageInScopeH;
  };

  // 应用缩放和平移变换
  const applyTransformations = (g) => {
    const zoomScaleWidth = svgSize.width / clipW; 
    const zoomScaleHeight = svgSize.height / clipH;
    const zoomScale = Math.min(zoomScaleWidth, zoomScaleHeight);

    g.attr("transform", `scale(${zoomScale}) translate(-${clipX}, -${clipY})`);
  };

  // 添加裁剪路径
  const addClipPath = (g) => {
    g.append('defs').append('clipPath')
      .attr('id', 'clip')
      .append('rect')
      .attr('x', clipX)
      .attr('y', clipY)
      .attr('width', clipW)
      .attr('height', clipH);
  };

对图片进行旋转

1 定义信号量

const [angle, setAngle] = createSignal(0);

2 定义相应函数

  // 旋转图像
  const rotateG = () => {
    const g = d3.select(svgRef()).select('#innerGroup'); // 使用 ID 选择 innerGroup
    const currentAngle = angle();
    const newAngle = currentAngle + 90;

    const zoomScaleWidth = svgSize.width / clipW;
    const zoomScaleHeight = svgSize.height / clipH;
    const zoomScale = Math.min(zoomScaleWidth, zoomScaleHeight);

    g.attr('transform', `rotate(${newAngle}, ${svgSize.width / 2}, ${svgSize.height / 2}) scale(${zoomScale}) translate(-${clipX}, -${clipY})`);
    setAngle(newAngle);
  };

3 绑定到按钮中

<button onClick={rotateG}>旋转图像</button>

绑定zoom事件

技巧:当svg中某元素已经绑定了缩放、拖拽等事件,可在这些元素外包裹一层g元素,对g元素进行的zoom操作不会和其中元素已定义的zoom操作相冲突

1 导入d3-zoom

import { zoom } from 'd3-zoom';

2 包裹新的g元素

const outerGroup = svg.append('g').attr('id', 'outerGroup'); // 新增外层 g

3 添加zoom事件监听器,绑定到svg中

// 添加缩放事件监听器
const zoomBehavior = zoom()
.scaleExtent([0.5, 5]) // 设置缩放范围
.on('zoom', (event) => {
  outerGroup.attr('transform', event.transform); // 应用缩放变换
});

svg.call(zoomBehavior); // 将缩放行为应用到SVG元素

完整代码

import { createSignal, onMount } from 'solid-js';
import * as d3 from 'd3';
import { zoom } from 'd3-zoom';

// 图像加载器组件
const ImageLoader = () => {
  // 用于保存SVG元素的引用
  const [svgRef, setSvgRef] = createSignal<SVGSVGElement | null>(null);
  // 保存当前旋转角度
  const [angle, setAngle] = createSignal(0);

  // 图像原始尺寸
  const imageSize = { width: 11920, height: 16850 };
  // 裁剪区域
  const croppedScope = { x: 5000, y: 10000, width: 5000, height: 5000 };
  // SVG显示区域尺寸
  const svgSize = { width: 500, height: 400 };

  // 用于计算当前图像在SVG中的显示区域
  let imageInScopeX, imageInScopeY, imageInScopeW, imageInScopeH;
  // 用于定义裁剪区域
  let clipX, clipY, clipW, clipH;

  // 计算缩放比例
  const calculateScale = () => {
    const imageW2H = imageSize.width / imageSize.height;
    const svgW2H = svgSize.width / svgSize.height;

    return imageW2H > svgW2H 
      ? svgSize.width / imageSize.width 
      : svgSize.height / imageSize.height;
  };

  // 根据缩放比例计算图像显示属性
  const calculateImageAttributes = (scale) => {
    imageInScopeW = imageSize.width * scale;
    imageInScopeH = imageSize.height * scale;
    imageInScopeX = (svgSize.width - imageInScopeW) / 2;
    imageInScopeY = (svgSize.height - imageInScopeH) / 2;
  };

  // 计算裁剪区域属性
  const calculateClipAttributes = () => {
    clipX = imageInScopeX + (croppedScope.x / imageSize.width) * imageInScopeW;
    clipY = imageInScopeY + (croppedScope.y / imageSize.height) * imageInScopeH;
    clipW = (croppedScope.width / imageSize.width) * imageInScopeW;
    clipH = (croppedScope.height / imageSize.height) * imageInScopeH;
  };

  // 应用缩放和平移变换
  const applyTransformations = (g) => {
    const zoomScaleWidth = svgSize.width / clipW; 
    const zoomScaleHeight = svgSize.height / clipH;
    const zoomScale = Math.min(zoomScaleWidth, zoomScaleHeight);

    g.attr("transform", `scale(${zoomScale}) translate(-${clipX}, -${clipY})`);
  };

  // 添加图像元素
  const addImageElement = (g) => {
    g.append('image')
      .attr('href', './src/assets/creatives/_1.jpg')
      .attr('x', imageInScopeX)
      .attr('y', imageInScopeY)
      .attr('width', imageInScopeW)
      .attr('height', imageInScopeH)
      .attr('clip-path', 'url(#clip)');
  };

  // 添加裁剪路径
  const addClipPath = (g) => {
    g.append('defs').append('clipPath')
      .attr('id', 'clip')
      .append('rect')
      .attr('x', clipX)
      .attr('y', clipY)
      .attr('width', clipW)
      .attr('height', clipH);
  };

  // 在组件挂载时初始化SVG和图像
  onMount(() => {
    const svg = d3.select(svgRef())
      .attr('width', svgSize.width)
      .attr('height', svgSize.height);
  
    // 创建外层g元素
    const outerGroup = svg.append('g').attr('id', 'outerGroup'); // 新增外层 g
  
    // 内部的 g 元素 (现有的 g)
    const innerGroup = outerGroup.append('g').attr('id', 'innerGroup'); // 设置 ID 为 innerGroup
  
    const scale = calculateScale();
    calculateImageAttributes(scale);
    calculateClipAttributes();
  
    addImageElement(innerGroup); // 修改为使用 innerGroup
    addClipPath(innerGroup); // 修改为使用 innerGroup
    applyTransformations(innerGroup); // 修改为使用 innerGroup

    // 添加缩放事件监听器
    const zoomBehavior = zoom()
    .scaleExtent([0.5, 5]) // 设置缩放范围
    .on('zoom', (event) => {
      outerGroup.attr('transform', event.transform); // 应用缩放变换
    });

    svg.call(zoomBehavior); // 将缩放行为应用到SVG元素
  });
  

  // 旋转图像
  const rotateG = () => {
    const g = d3.select(svgRef()).select('#innerGroup'); // 使用 ID 选择 innerGroup
    const currentAngle = angle();
    const newAngle = currentAngle + 90;

    const zoomScaleWidth = svgSize.width / clipW;
    const zoomScaleHeight = svgSize.height / clipH;
    const zoomScale = Math.min(zoomScaleWidth, zoomScaleHeight);

    g.attr('transform', `rotate(${newAngle}, ${svgSize.width / 2}, ${svgSize.height / 2}) scale(${zoomScale}) translate(-${clipX}, -${clipY})`);
    setAngle(newAngle);
  };

  // 渲染SVG和按钮
  return (
    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <button onClick={rotateG}>旋转图像</button>
      <svg ref={setSvgRef} style={{ border: '1px solid black' }} />
    </div>
  );
};

export default ImageLoader;
posted @ 2024-09-24 19:39  梧桐灯下江楚滢  阅读(26)  评论(0)    收藏  举报