对图片指定范围的区域进行填充显示
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;
标签:24,const,attr,知识,height,width,svgSize,SolidJS,imageSize
From: https://www.cnblogs.com/Frey-Li/p/18429867