如果您遇到过,您会知道我在说什么。在SVG中处理这种类型的梯度真的很烦人。
查看最终编码器或遵循分步过程。
天然SVG方法
svg <path>
,以及其他SVG笔触,例如<polygon>
和<polyline>
,只能使用linearGradient
和radialGradient
进行颜色。
<!-- Here and below we're using the same pre-defined path -->
<svg width="0" height="0" viewBox="0 0 250 250">
<defs>
<path id="gradient-path" d="M36.5,91.2C-7.5,185.5,99.3,224.4,170,203.1c55-16.6,57.8-87.4,1.6-104C71,69.5,9.4,207.7,46,228.6c62.7,35.8,189.7-116,133-211"/>
</defs>
</svg>
<!-- Coloring the path with native SVG gradient -->
<svg id="demo" viewBox="0 0 500 250">
<defs>
<linearGradient id="linear-grad">
<stop offset="0" stop-color="#f7ff00"/>
<stop offset="1" stop-color="#db36a4"/>
</linearGradient>
<radialGradient id="radial-grad">
<stop offset="0" stop-color="#f7ff00"/>
<stop offset="1" stop-color="#db36a4"/>
</radialGradient>
</defs>
<use xlink:href="#gradient-path" stroke="url(#linear-grad)" fill="none" stroke-width="15"/>
<use xlink:href="#gradient-path" stroke="url(#radial-grad)" x="250" fill="none" stroke-width="15"/>
</svg>
虽然两种梯度类型都有lots of settings,但颜色分布始终遵循直线。
如果我们希望颜色沿曲线变化,我们需要CSS或JavaScript的帮助。这是一个普遍的问题,我们有一些解决方案。 D3.js库的创建者Mike Bostock已共享this方法。帕特里克·卡森(Patrick Cason)创建了一个基于Mike解决方案的small library,但没有D3.js依赖性。其他人涵盖了更具体的情况。例如,阿米特·希恩(Amit Sheen)发布了有关仅CSS的trick,以沿着圆形路径构建和动画梯度。
用GSAP创建梯度
作为A Big Fan GSAP的经常用户,我也喜欢贡献。
我们将使用GSAP及其MotionPathPlugin沿给定的<path>
分发<circle>
元素,并为这些圆圈涂色以组成梯度。
定义了参考<path>
并添加both GSAP files后,我们创建元素,沿路径定位,然后对其应用梯度颜色。
<svg viewBox="0 0 250 250">
<defs>
<path id="gradient-path" d="M36.5,91.2C-7.5,185.5,99.3,224.4,170,203.1c55-16.6,57.8-87.4,1.6-104C71,69.5,9.4,207.7,46,228.6c62.7,35.8,189.7-116,133-211"/>
</defs>
<g class="dots">
// to hold all the circles
</g>
</svg>
const strokeWidth = 15;
const colors = ["#f7ff00", "#db36a4"];
const gradientPath = document.querySelector("#gradient-path");
// const numberOfDots = Math.ceil(gradientPath.getTotalLength() / strokeWidth); // for circles to be placed back-to-back
const dotsDensity = .5 * strokeWidth;
const numberOfDots = Math.ceil(dotsDensity * gradientPath.getTotalLength() / strokeWidth);
要找出可以使用getTotalLength()
的圆数。该天然JS方法检索路径的总长度。如果我们将gradientPath.getTotalLength() / strokeWidth
作为许多点,它们会背靠背。通过增加dotsDensity
,我们可以获得一条平滑的线:
我们使用本机JS appendChild
方法创建圆圈,并使用motionPath plugin定义圆的位置。
const dotsGroup = document.querySelector(".dots");
createBasicGradient(dotsGroup);
function createBasicGradient(g) {
for (let idx = 0; idx < numberOfDots; idx++) {
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
g.appendChild(circle);
gsap.set(circle, {
motionPath:
path: gradientPath, // the target path
start: idx / numberOfDots, // the position on target path
end: idx / numberOfDots,
},
attr: {
cx: 0, // the position is defined by transform attribute, so we keep circle center on (0, 0) point
cy: 0,
r: .5 * strokeWidth, // to compose strokeWidth
fill: gsap.utils.interpolate(colors, (idx / numberOfDots)) // linear interpolation between 2 (or more!) given colors
}
});
}
}
- 提示#1
我使用GSAPinterpolate
实用程序在两种颜色之间使用简单的线性插值。您可以轻松地为colors
数组添加更多颜色,或构建自定义功能以计算(idx / numberOfDots)
值的颜色。
,除非我们使用圆形linecap,否则我们可能需要修复行程技巧。
有不同的方法。例如,我们可以用原始路径掩盖点。
<path id="gradient-path" d="M36.5,91.2C-7.5,185.5,99.3,224.4,170,203.1c55-16.6,57.8-87.4,1.6-104C71,69.5,9.4,207.7,46,228.6c62.7,35.8,189.7-116,133-211"/>
<mask id="gradient-path-clip">
<use xlink:href="#gradient-path" stroke-width="15" fill="none" stroke="white"/>
</mask>
...
<g mask="url(#gradient-path-clip)" class="dots">
</g>
沿行程的基本梯度完成了! ð
- 提示#2
您可以轻松地用另一种形状替换<circle>
,并用矩形或其他形状构成梯度。如果这样做,您还将沿路径的粒子对齐:motionPath: { ... align: gradientPath, alignOrigin: [.5, .5], ... }
高级技术
处理中风交叉点
假设我们希望我们的道路看起来像是一个适当的结,而中风会在底部“下方”。
我们可以重新排序点,并将“背”点附加到其他人面前。然后,我们使用重新装置索引代替原始顺序应用颜色和位置。
for (let idx = 0; idx < numberOfDots; idx++) {
// append circles in normal order
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
g.appendChild(circle);
// remap the index to move the some of the back dots to the middle of stroke length
let idxRemapped = idx;
if (idx < .1 * numberOfDots) {
idxRemapped += Math.ceil(.7 * numberOfDots); // [ .0 .. .1 ] to [ .7 .. .8 ]
} else {
if (idx < .8 * numberOfDots) {
idxRemapped -= Math.ceil(.1 * numberOfDots); // [ .1 .. .8 ] to [ .0 .. .7 ]
}
}
// apply position and color using idxRemapped
gsap.set(circle, {
// ...
});
}
索引重新映射是特定路径的特定的,但希望您能得到这个想法:)
不同的中风宽度
构建动态路径宽度非常简单,我们只需要根据索引更改点大小即可。
gsap.set(circle, {
attr: {
// ...
r: .5 * strokeWidth + .02 * idx,
}
})
,当然,我们可以使用更多的精美功能来计算点大小。
动画渐变
GSAP是一个动画平台,MotionPathplugin的设计是沿路径移动的事物。因此,我们可以轻松地对其进行动画动画,而不是设置点位置。
for (let idx = 0; idx < numberOfDots; idx++) {
// create dot and set static attributes like before
const circle = document.createElementNS("http://www.w3.org/2000/svg", "circle");
g.appendChild(circle);
gsap.set(circle, {
// motionPath: {
// path: gradientPath,
// start: idxRemapped / numberOfDots,
// end: idxRemapped / numberOfDots,
// },
attr: {
cx: 0,
cy: 0,
r: .5 * strokeWidth,
fill: gsap.utils.interpolate(colors, (idxRemapped / numberOfDots))
}
});
// add position-on-path animation
gsap.to(circle, {
motionPath: {
path: gradientPath // position along the path
},
duration: 2, // the time each dot takes to travel the whole path
ease: "none",
repeat: -1 // loop the animation
}).progress(idx / numberOfDots); // each dot start moving from their own position
}
很容易将梯度动画与其他效果相结合。在某些情况下,您可能需要增加点密度或添加掩蔽。
,你有! ð
我收集了单个Codepen上的所有示例,订阅更新!