有谁知道如何使用 SVG 创建以下设计?我有 SVG 格式的路径图像,因此在 SVGR 中使用它,我创建了路径,现在我需要在路径上以随机顺序添加圆圈。任何线索将不胜感激。
编辑: 路径的 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>
如有任何帮助,我们将不胜感激。
不需要任何脚本(除了创建 SVG)
<circle>
元素,其中包含 <animateMotion>
<mpath href="#PATH"></mpath>
keyTimes
定义从 0 到 1keyPoints
将浮点值定位在 0 和 1dur
设置为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>
您可以使用这个简单的例程计算点的位置:
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>
兄弟
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>
)
}