利用svg显示不规则图形,实现用户点击交互:
需求评审时产品掏出了甲方的区域图,要求实现点击后出来对应弹窗信息,但是由于是不规则图片,交由前端实现,现将实现方式记录下来
例如:项目场景:有下方有三块区域是办公区域,需要鼠标点击后出来对应区域信息。
实现步骤
1、ui将区域图设计稿导出为svg格式,并导入到项目中
###注意###:将文件导入到项目中,由于是在react上开发,所以xmlns:xlink,xmlns:href等带有冒号的代码要改为xmlnsXlink格式,将styles文件单独拎出去,放在css中,在所引入的文件将css引入。image标签引入的图片为base64导致代码太大,可以随便找个在线网站将base64转换为png引入,例如:
<image id="bg" width="100%" height="100%" xlinkHref={require("@/assets/img/breedAndProcess/middleImg.png")} />
分析svg,实现交互:
缺点:本来是想当移入svg后,区域背景改变,点击进行交互,但是rect标签和text标签分别是独立的,移入text标签后,下方的rect标签的hover就失效了,只能退而求次,当hover的是text的时候颜色改变,点击事件也绑定在text上了
这里分析一下ui给出的svg规律,rect是每个小区域,相当于我们的div,紧接着的text是附在上面的文字,每个g标签下的最后一个rect,id为line的标签是包含整个小区域的盒子。
所以,知道了这些,我们可以将svg绑定ref,通过querySelectorAll所以g标签下的所有text,并绑定上点击事件。
<g id="list" className="list">
<rect id="矩形_927" data-name="矩形 927"
className="delivery-cls-1" x="156" y="33" width="108" height="32" rx="2" ry="2" />
<text id="牛舍01" className="delivery-cls-2" x="183.827" y="56.384" data-name="牛舍01">牛舍01</text>
<rect id="矩形_927-2" data-name="矩形 927" className="delivery-cls-1" x="156" y="69" width="108" height="32" rx="2" ry="2" />
<text id="牛舍02" className="delivery-cls-2" x="183.827" y="92.384" data-name="牛舍02">牛舍02</text>
<rect id="line" className="delivery-cls-3" x="150" y="27" width="119" height="151" rx="2" ry="2" />
</g>
<g id="list-2" data-name="list">
<rect id="牛舍05" className="delivery-cls-1" x="29" y="189" width="108" height="32" rx="2" ry="2" />
<text id="牛舍05-2" data-name="牛舍05" className="delivery-cls-2" x="56.827" y="212.384">牛舍05</text>
<rect id="line-2" data-name="line" className="delivery-cls-3" x="23" y="183" width="119" height="151" rx="2" ry="2" />
</g>
...
if (svgRef.current) {
const gElements = svgRef.current.querySelectorAll('g'); // 获取所有 <g> 元素
gElements.forEach((gElement) => {
const textElements = gElement.querySelectorAll('text'); // 获取每个 <g> 元素下的所有 <text> 元素
textElements.forEach((textElement, index, array) => {
textElement.addEventListener('click', (event) => {
handleTextClick(event, data)
});
});
});
}
附:点击后的popper组件实现
import React, { useState } from 'react'
import './index.scss';
export default function index({ width, height, top, left, show, children }) {
return (
<div className='cjm_popover' style={{
visibility: show,
width: width || '180px',
height: height || '190px',
top: top || '50%',
left: left || '50%'
}} >{children}</div>
)
}
-------------------使用----------------------
useEffect(() => {
document.addEventListener('click', handleGlobalClick);
return () => {
document.removeEventListener('click', handleGlobalClick);
};
}, []);
// 全局监听点击,如果点击了别的区域关闭popper
const handleGlobalClick = (event) => {
let config = { ...popConfig, show: 'hidden' }
setPopConfig(config)
};
//打开传入 clientX, clientY位置
const { clientX, clientY } = event;
setPopConfig({
show: 'visible',
top: `${clientY}px`,
left: `${clientX}px`,
})
<Popover {...popConfig} >
<div className={styles.noData}>
暂无数据!
</div>
</Popover>
附2 拖拽缩放
如果希望元素可以实现拖拽缩放可以引入panzoom.js
import React, { useRef, useEffect } from "react";
import panzoom from "panzoom";
export default function index() {
const svgContainerRef = useRef(null);
const scene = useRef(null);
let panzoomInstance;
useEffect(() => {
const svgContainer = svgContainerRef.current;
// 创建 panzoom 实例并将其附加到 SVG 容器上
panzoomInstance = panzoom(svgContainer, {
smoothScroll: false, // 禁用平滑滚动以增加拖拽的响应性
zoomDoubleClickSpeed: 1, // 设置双击缩放速度
});
// 清理 panzoom 实例
return () => {
if (panzoomInstance) {
panzoomInstance.dispose();
panzoomInstance = null;
}
};
}, []);
return (
<div
ref={svgContainerRef}
style={{
width: "400px",
height: "400px",
border: "1px solid black",
overflow: "hidden",
zIndex: 120,
cursor: "pointer",
}}
onClick={() => {
console.log(11, "11");
}}
>
</div>
);
}