如何使用 SVG 在 React Native 中创建以下设计?

问题描述 投票:0回答:3

有谁知道如何使用 SVG 创建以下设计?我有 SVG 格式的路径图像,因此在 SVGR 中使用它,我创建了路径,现在我需要在路径上以随机顺序添加圆圈。任何线索将不胜感激。

enter image description here

编辑: 路径的 SVG 代码

    <Svg
      xmlns="http://www.w3.org/2000/svg"
      width={395.215}
      height={180.855}
      viewBox="0 0 395.215 180.855"
      {...props}>

      <Path
        fill="none"
        stroke="#ecece9"
        strokeWidth={15}
        d="M7.146 160.744c16.087-50.465 64.229-94.319 115.671-92.656s60.239 86.363 89.6 100.623 47.453-4.982 51.241-65.74 24.858-126.75 61.62-79.227 62.437 90.632 62.437 115.574"
      />
    </Svg>

如有任何帮助,我们将不胜感激。

react-native svg
3个回答
3
投票

不需要任何脚本(除了创建 SVG)

  • 创建
    <circle>
    元素,其中包含
    <animateMotion>
  • 以及对单个路径的运动路径引用:
    <mpath href="#PATH"></mpath>
  • keyTimes
    定义从 01
  • 的路径“长度”
  • keyPoints
    将浮点值定位在 01
  • 之间的圆
  • dur
    设置为
    0.001s
    即可即时显示

<style>
  svg{
    height:180px;
    background:pink;
  }
</style>
<svg viewBox="0 0 400 200">
  <style>
   circle{ r:15 }
   [done]{ fill:green }
   [todo]{ fill:yellow }
  </style>
  <path id="PATH" fill="none" stroke="grey" stroke-width="15" d="M7 161c16-50 64-94 116-93s60 86 90 101s47-5 51-66s25-127 62-79s62 91 62 116"></path>
  <circle done>
    <animateMotion dur="1s" fill="freeze" keyPoints="0;.1" keyTimes="0;1" calcMode="linear"><mpath href="#PATH"></mpath></animateMotion>
  </circle>
  <circle done>
    <animateMotion dur="1s" fill="freeze" keyPoints="0;.2" keyTimes="0;1" calcMode="linear"><mpath href="#PATH"></mpath></animateMotion>
  </circle>
  <circle done>
    <animateMotion dur="1s" fill="freeze" keyPoints="0;.3" keyTimes="0;1" calcMode="linear"><mpath href="#PATH"></mpath></animateMotion>
  </circle>
  <g><circle todo></circle>
     <text text-anchor="middle" alignment-baseline="middle">04</text>
      <animateMotion dur="1s" fill="freeze" keyPoints="0;.4" keyTimes="0;1" calcMode="linear"><mpath href="#PATH"></mpath></animateMotion>
  </g>
  <g><circle todo></circle>
     <text text-anchor="middle" alignment-baseline="middle">05</text>
      <animateMotion dur="0.01s" fill="freeze" keyPoints="0;.5" keyTimes="0;1" calcMode="linear"><mpath href="#PATH"></mpath></animateMotion>
  </g>
</svg>

更新:我接触过一些本地人!网络组件:

<svg-spread-on-path duration="1" path="P">
  <style>
    svg    { width: 100%; height:180px; background: pink }
    circle { r: 15}
    [done] { fill: green }
    [todo] { fill: yellow }
  </style>
  <path id="P" fill="none" stroke="grey" stroke-width="15" d="M7 161c16-50 64-94 116-93s60 86 90 101s47-5 51-66s25-127 62-79s62 91 62 116"></path>
  <circle done />
  <circle done />
  <circle done />
  <circle done />
  <circle todo />
  <circle todo />
  <circle todo />
</svg-spread-on-path>
<script>
  customElements.define("svg-spread-on-path", class extends HTMLElement {
    constructor() {
      let svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
      svg.setAttribute("viewBox", "0 0 400 200");
      super().attachShadow({mode: "open"}).append(this.svg = svg);
    }
    connectedCallback() {
      setTimeout(() => {
        this.svg.innerHTML = this.innerHTML; //   svg.append(...this.children);
        let circles = [...this.svg.querySelectorAll("circle")];
        this.svg.append(...circles.map((circle, idx) => {
          let path = this.getAttribute("path");
          let duration = this.getAttribute("duration") || 1 / 100;
          let state = circle.hasAttribute("done") ? "done" : "todo";
          let label = idx;
          let keypoint = idx * 1 / (circles.length - 1);
          let group = document.createElementNS("http://www.w3.org/2000/svg", "g");
          group.innerHTML = `<circle ${state}></circle>` +
            `<text text-anchor="middle" alignment-baseline="middle">${label}</text>` +
            `<animateMotion dur="${duration}" repeatCount="1" fill="freeze" keyPoints="0;${keypoint}" keyTimes="0;1" calcMode="linear"><mpath href="#${path}"></mpath></animateMotion>`;
          circle.remove();
          group.onclick = evt => group.querySelector("animateMotion").beginElement();
          return group;
        }));
      });
    }
  });
</script>


1
投票

您可以使用这个简单的例程计算点的位置:

const getPointsAlongPath = (path, number) => {
 const length = path.getTotalLength();
 return (new Array(number)).fill(0).map((_, i) => path.getPointAtLength(length / (number - 1) * i))
};

,其中

path
是路径元素,
number
是要沿路径放置的点的数量。

React 代码可能是这样的:

const PathWithPoints({path, number}) => {
  const pathRef = useRef();
  const points = getPointsAlongPath(pathRef.current, number);

  return (
    <svg>
      <path d={path} ref={pathRef} ... />
      {points.map(({x, y}) => (<circle cx={x} cy={y} r={...} fill={...} />)}
    </svg>
  )
}

const getPointsAlongPath = (path, number) => {
 const length = path.getTotalLength();
 return (new Array(number)).fill(0).map((_, i) => path.getPointAtLength(length / (number - 1) * i))
};

const svg = d3.select('svg');
const path = svg.select('path');
const points = getPointsAlongPath(path.node(), 8);
points.forEach(({x, y}) => svg.append('circle').attr('cx', x).attr('cy', y)
.attr('r', 15).style('fill', 'red'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg
      width="450"
      height="180"
      viewBox="0 0 395.215 180.855"
>
<path
        fill="none"
        stroke="#ecece9"
        stroke-width="15"
        d="M15 160.744c16.087-50.465 64.229-94.319 115.671-92.656s60.239 86.363 89.6 100.623 47.453-4.982 51.241-65.74 24.858-126.75 61.62-79.227 62.437 90.632 62.437 115.574"
      />
 </Svg>


0
投票

兄弟

import React, { useState } from "react";
import { TouchableOpacity, View } from "react-native";
import { Circle, Path, Svg } from "react-native-svg";
import { SVGPoint } from "react-native-svg/lib/typescript/elements/Shape";

export default function ScreenTwo(){
  
  const pathRef = React.createRef<Path>()
  const [sliderPoints, setSliderPoints] = useState<{id:number, point:SVGPoint}[]>() 

  const setPoints = () => {
    const numPoints = 5
    setSliderPoints([])
    for(let i=0; i<=numPoints; i+=1){
      const pathLength = pathRef.current?.getTotalLength()
      const point = pathRef.current?.getPointAtLength(pathLength? pathLength/numPoints*i : 0)
      setSliderPoints(prev => point&&prev?[...prev, {id:i,point:point}]:[])

    }
  }
    return(
        <View style={{flex:1, backgroundColor:'grey', alignItems:'center', justifyContent:'center'}}>
            <View style={{width:'90%', height:'70%'}}  >
                <Svg viewBox="0 0 100 100">
                  <Path ref={pathRef} d='M3 50 L 97 50' strokeWidth={2} stroke={'black'}/>
                  {sliderPoints? sliderPoints.map(res => (
                    <Circle cx={res.point.x} cy={res.point.y} r={3} key={res.id} fill={'blue'}/>
                  )):null}
                </Svg>
            </View>
            <TouchableOpacity style={{height:100, width:100, backgroundColor:'purple'}} onPress={() => setPoints()}/>
        </View>
    )
}

结果

© www.soinside.com 2019 - 2024. All rights reserved.