利用svg显示不规则图形,实现用户点击交互

利用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>
  );
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值