问题描述
function createPath() {
const
dimensions = getwindowDimensions(),svg = document.querySelector( `svg` ),path = document.createElementNS( `http://www.w3.org/2000/svg`,`path` );
dimensions[0] = dimensions[0]; dimensions[1] = dimensions[1];
svg.appendChild( path );
path.setAttribute(
`d`,`M ` +
`${getRandomNumber(dimensions[0])} `+`${getRandomNumber(dimensions[1])} `+
`C `+
`${getRandomNumber(dimensions[0])} `+`${getRandomNumber( dimensions[1])},`+
`${getRandomNumber(dimensions[0])} `+`${getRandomNumber( dimensions[1])},`+
`${getRandomNumber(dimensions[0])} `+`${getRandomNumber( dimensions[1])} `
)
for( let i = 0; i < 100; i++ ) {
path.setAttribute(
`d`,path.getAttribute( `d` ) +
`S `+`${getRandomNumber(dimensions[0])} `+`${getRandomNumber(dimensions[1])},`+
`${getRandomNumber(dimensions[0])} `+`${getRandomNumber(dimensions[1])} `
)
}
}
setInterval( setSVGDimensions,10 ); setInterval( positionPath,10 );
createPath();
* { Box-sizing: border-Box; margin: 0; padding: 0; }
html,body { height: 100%; }
body {
display: flex; justify-content: center; align-items: center;
}
svg {
border-radius: 1rem; background-color: rgba( 95%,95%,0.5 );
filter: blur( 1rem );
animation-name: blur; animation-duration: 1s;
animation-timing-function: ease-in-out; animation-fill-mode: forwards;
stroke-linecap: round; stroke-linejoin: round;
stroke-width: 0.25rem; fill: none;
}
@keyframes blur {
100% { filter: blur( 0rem ); }
}
path {
animation-name: grow;
animation-duration: 2500s;
animation-timing-function: cubic-bezier( 0.75,0.25,1 );
stroke-dasharray: 1000000;
stroke-dashoffset: 1000000;
stroke: rgba( 0%,100%,75%,0.75 );
}
@keyframes grow {
100% { stroke-dashoffset: 0; }
}
<svg></svg>
<script>
function getRandomNumber( max ) { return Math.floor( Math.random() * max ); }
function getwindowDimensions() {
const
dimensions = [],windowWidth = document.documentElement.clientWidth,windowHeight = document.documentElement.clientHeight;
dimensions.push( windowWidth,windowHeight );
return dimensions;
}
function setSVGDimensions() {
const
dimensions = getwindowDimensions(),svg = document.querySelector( `svg` );
svg.style.width = dimensions[0] - 10; svg.style.height = dimensions[1] - 10;
}
function positionPath() {
const
dimensions = getwindowDimensions(),path = document.querySelector( `path` );
path.setAttribute(
`transform`,`
scale( 0.5 ) translate( ${ dimensions[0] / 2 },${ dimensions[1] / 3 } )
`
)
}
</script>
除了某些曲线的锐度之外,这是所需的行为。半径太小,角度太锐。我们想要更宽更平滑的曲线。例如,在此屏幕截图中,问题区域被圈出。
在下图中,请注意红色圆圈具有非常尖锐的曲线,而绿色圆圈则是更宽更平滑的曲线:
有没有办法可以使用 JavaScript 来防止创建尖锐曲线(红色圆圈)并让算法只创建更宽的曲线(绿色圆圈) )?
解决方法
我添加了一些函数来检查最后两点和下一个点之间的角度是否不小于 MIN_ANGLE
。现在是 60 度,但可以更宽以获得更大的曲线半径。
我还添加了 MIN_DISTANCE
,因为两点之间的距离太短也会提供尖锐的曲线。
let lastTwoPoints = [];
const MIN_ANGLE = 60;
const MIN_DISTANCE = (Math.min(...getWindowDimensions()))/10;
function getPoint(){
let point = [getRandomNumber(getWindowDimensions()[0]),getRandomNumber(getWindowDimensions()[1])];
if(lastTwoPoints.length < 2){
lastTwoPoints.push(point);
} else {
if(getAngle(...lastTwoPoints,point) < MIN_ANGLE || getDistance(lastTwoPoints[1],point) < MIN_DISTANCE){
point = getPoint();
} else {
lastTwoPoints.shift();
lastTwoPoints.push(point);
}
}
return point;
}
function pointString(){
let point = getPoint();
return `${point[0]} ${point[1]} `;
}
function getDistance(pointA,pointB){
return Math.sqrt((pointA[0] - pointB[0])**2 + (pointA[1] - pointB[1])**2);
}
function getAngle(pointA,pointB,pointC){ // angle to pointB
let a = getDistance(pointA,pointB);
let b = getDistance(pointB,pointC);
let c = getDistance(pointC,pointA);
return Math.acos((a*a + b*b - c*c)/(2*a*b))*(180/Math.PI);
}
function createPath() {
const
dimensions = getWindowDimensions(),svg = document.querySelector( `svg` ),path = document.createElementNS( `http://www.w3.org/2000/svg`,`path` );
dimensions[0] = dimensions[0]; dimensions[1] = dimensions[1];
svg.appendChild( path );
path.setAttribute(
`d`,`M ` +
`${pointString()}`+
`C `+
`${pointString()}`+
`${pointString()}`+
`${pointString()}`
)
for( let i = 0; i < 100; i++ ) {
path.setAttribute(
`d`,path.getAttribute( `d` ) +
`S `+`${pointString()}`+
`${pointString()}`
)
}
}
setInterval( setSVGDimensions,10 ); setInterval( positionPath,10 );
createPath();
function getRandomNumber( max ) { return Math.floor( Math.random() * max ); }
function getWindowDimensions() {
const
dimensions = [],windowWidth = document.documentElement.clientWidth,windowHeight = document.documentElement.clientHeight;
dimensions.push( windowWidth,windowHeight );
return dimensions;
}
function setSVGDimensions() {
const
dimensions = getWindowDimensions(),svg = document.querySelector( `svg` );
svg.style.width = dimensions[0] - 10; svg.style.height = dimensions[1] - 10;
}
function positionPath() {
const
dimensions = getWindowDimensions(),path = document.querySelector( `path` );
path.setAttribute(
`transform`,`
scale( 0.5 ) translate( ${ dimensions[0] / 2 },${ dimensions[1] / 3 } )
`
)
}
* { box-sizing: border-box; margin: 0; padding: 0; }
html,body { height: 100%; }
body {
display: flex; justify-content: center; align-items: center;
}
svg {
border-radius: 1rem; background-color: rgba( 95%,95%,0.5 );
filter: blur( 1rem );
animation-name: blur; animation-duration: 1s;
animation-timing-function: ease-in-out; animation-fill-mode: forwards;
stroke-linecap: round; stroke-linejoin: round;
stroke-width: 0.25rem; fill: none;
}
@keyframes blur {
100% { filter: blur( 0rem ); }
}
path {
animation-name: grow;
animation-duration: 2500s;
animation-timing-function: cubic-bezier( 0.75,0.25,1 );
stroke-dasharray: 1000000;
stroke-dashoffset: 1000000;
stroke: rgba( 0%,100%,75%,0.75 );
}
@keyframes grow {
100% { stroke-dashoffset: 0; }
}
<svg></svg>
我已经清理了代码,添加了 MAX_DISTANCE
来检查:
let lastTwoPoints = [];
const W = document.documentElement.clientWidth;
const H = document.documentElement.clientHeight;
const MIN_ANGLE = 60;
const MIN_DISTANCE = (Math.min(W,H))/20;
const MAX_DISTANCE = (Math.min(W,H))/4;
let svg = document.querySelector('svg');
let path = document.querySelector('path');
svg.style.width = W;
svg.style.height = H;
createPath();
function getPoint(){
let x = getRandomNumber(W*0.6) + W*0.2;
let y = getRandomNumber(H*0.6) + H*0.2;
let point = [x,y];
if(lastTwoPoints.length < 2){
lastTwoPoints.push(point);
} else {
if(getAngle(...lastTwoPoints,point) < MIN_ANGLE
|| getDistance(lastTwoPoints[1],point) < MIN_DISTANCE
|| getDistance(lastTwoPoints[1],point) > MAX_DISTANCE){
point = getPoint();
} else {
lastTwoPoints.shift();
lastTwoPoints.push(point);
}
}
return point;
}
function pointString(){
let point = getPoint();
return `${point[0]} ${point[1]} `;
}
function getDistance(pointA,pointA);
return Math.acos((a*a + b*b - c*c)/(2*a*b))*(180/Math.PI);
}
function createPath() {
let path_string = `M ${pointString()} C ${pointString()} ${pointString()} ${pointString()}`;
for( let i = 0; i < 100; i++ ) {
path_string += `S ${pointString()} ${pointString()} `
}
path.setAttribute('d',path_string);
}
function getRandomNumber(max) { return Math.floor( Math.random() * max ); }
<svg fill="none" stroke="black">
<path d=""/>
</svg>