上一章講述瞭如何利用C4D圖切割製作閃光效果。本章將講述如何在上章文章描述的效果基礎上加入橢圓動態效果以及閃動控制。
如下圖所示,橢圓運動主要分成兩部分:外圈運動、內圈運動。其中內圈運動看似一個橢圓,實際上是由兩個橢圓不同角度組成的。紅色指向的是運動的小球,綠箭頭是小球運動的方向。下面將講述如何展示圓球運動,主要是運用canvas畫布製作的,探測小球的運動軌跡,以及到點則控制對應塊的閃爍。
一、橢圓運動函數
先了解一下橢圓運動函數circleRunEllipse整體,主要使用函數:
ellipseRun — 橢圓運動執行
drawCircle — 繪畫指定位置的運動小球點
clearRun — 停止小球運動
drawEllipse — 畫橢圓運動軌跡,用於調試匹配設計稿
drawPoint — 起始點、終止點調試
`/**
* 首頁運維與服務背景圖動畫
* @param canvasID 畫布ID
* @param ellipseHeight 橢圓運動高度
* @param ellipseWidth 橢圓運動寬度
* @param type 哪一個圈,大、中、小--【1 | 2 | 3】
*/export function circleRunEllipse(canvasID, ellipseHeight, ellipseWidth, type) { // 基礎變量
let canvas: any; if (document.getElementById(canvasID)) {
canvas = document.getElementById(canvasID);
} else { return ;
} let context = canvas.getContext('2d'); let width = canvas.width = 469; let height = canvas.height = 1017; let animationFrame = null; // 記錄執行的動畫,用於取消
// 橢圓運動
let circleX = width / 2 - 127; // 橢圓運動中心點
let circleY = height / 2; let ellipseA = ellipseWidth; // 長軸a
let ellipseB = ellipseHeight; // 短軸b
let speed = 0.012; // 控制運動速度
let ellipseTime = 0; // 控制運動時間變化
/**
* 橢圓運動執行
*/
function ellipseRun() { if (animationFrame) { window.cancelAnimationFrame(animationFrame);
}
animationFrame = window.requestAnimationFrame(ellipseRun);
context.clearRect(0, 0, width, height); // drawEllipse(circleX, circleY);
drawCircle(circleX + ellipseA * Math.cos(ellipseTime), circleY + ellipseB * Math.sin(ellipseTime)); if (type === 1) {
context.clearRect(0, 80, 35, 55); // 右側點(25,80),高寬
context.clearRect(0, 880, 10, 5); // 左側點(0,885)
// 結束點21.09
ellipseTime += speed; if (ellipseTime > 21.09) { if (!clearAnimationFrame()) { document.getElementsByClassName('service-cloud1')[0].className += ' service-cloud'; document.getElementsByClassName('service-cloud4')[0].className = 'service-cloud4';
} else { return ;
}
ellipseTime = 16.8;
clearRun(5);
}
} else if (type === 2) {
context.clearRect(0, 720, 210, 200); // 左側點(208,720),高寬
context.clearRect(0, 308, 20, 415); // 右側點(2,308),高寬
// 結束點16.29
ellipseTime -= speed; if (ellipseTime < 16.29) { if (!clearAnimationFrame()) { document.getElementsByClassName('service-cloud2')[0].className = 'service-cloud2';
} else { return ;
}
ellipseTime = 19.45;
clearRun(5);
}
} else {
context.clearRect(0, 300, 43, 400); // 左側點(40,700),高寬
context.clearRect(0, 0, 175, 307); // 右側點(175,305),高寬
context.clearRect(142, 716, 30, 62); // 服務器右側點(142,778),高寬,左側點(169,716)
// 結束點18.08
ellipseTime -= speed; if (ellipseTime < 18.08) { if (!clearAnimationFrame()) { document.getElementsByClassName('service-cloud3')[0].className += ' service-cloud';
} else { return ;
}
ellipseTime = 21.24;
clearRun(5);
}
}
} /**
* 畫實體圓,描述位置
*/
function drawCircle(x, y) {
context.save();
context.fillStyle = '#0ff';
context.globalAlpha = 0.92;
context.beginPath();
context.arc(x, y, 3.5, 0, Math.PI * 2); // 半徑3
context.closePath();
context.fill();
context.restore();
} /**
* 畫橢圓,用於匹配設計稿路徑
* 1、畫橢圓,使用lineTo,把橢圓分割許多片段
* 2、橢圓的三角函數表達式 x = a*cos(t), y = b * sin(t);
*/
function drawEllipse(x, y) { // 這樣可以使得每次循環所繪製的路徑(弧線)接近1像素
let step = (ellipseA > ellipseB) ? 1 / ellipseA : 1 / ellipseB;
context.save();
context.strokeStyle = 'blue';
context.beginPath();
context.moveTo(x + ellipseA, y); for (let i = 0; i < Math.PI * 2; i += step) {
context.lineTo(x + ellipseA * Math.cos(i), y + ellipseB * Math.sin(i));
}
context.closePath();
context.stroke();
context.restore();
} /**
* 定點,用於消除隱藏多餘路徑
*/
function drawPoint(x, y) {
context.save();
context.fillStyle = 'red';
context.globalAlpha = 0.95;
context.beginPath();
context.arc(x, y, 3, 0, Math.PI * 2);
context.closePath();
context.fill();
context.restore();
} /**
* 停止運動
* @param time 時間,單位【秒】
*/
function clearRun(time) {
context.clearRect(0, 0, width, height); window.cancelAnimationFrame(animationFrame); let timer = null; // 提前一秒執行閃動動畫
let restartTimer = setTimeout(function() { if (clearAnimationFrame()) {
clearTimeout(restartTimer);
clearTimeout(timer); return ;
} if (type === 1) { document.getElementsByClassName('service-cloud1')[0].className = 'service-cloud1'; document.getElementsByClassName('service-cloud4')[0].className += ' service-cloud';
} else if (type === 2) { document.getElementsByClassName('service-cloud2')[0].className += ' service-cloud';
} else { document.getElementsByClassName('service-cloud3')[0].className = 'service-cloud3';
}
clearTimeout(restartTimer);
}, (time - 1) * 1000);
timer = setTimeout(function() {
animationFrame = window.requestAnimationFrame(ellipseRun);
clearTimeout(timer);
}, time * 1000);
} /**
* 清除運動
*/
function clearAnimationFrame() { ... } // 指定開始點執行橢圓運動
if (type === 1) {
ellipseTime = 16.8; // ellipseTime = 21.09; // 結束點
if (document.getElementsByClassName('service-cloud4').length > 0) { document.getElementsByClassName('service-cloud4')[0].className += ' service-cloud';
}
} else if (type === 2) {
ellipseTime = 19.45; // ellipseTime = 16.29; // 結束點
if (document.getElementsByClassName('service-cloud2').length > 0) { document.getElementsByClassName('service-cloud2')[0].className += ' service-cloud';
}
} else if (type === 3) {
ellipseTime = 21.24; // ellipseTime = 18.08; // 結束點
} let startTimer = setTimeout(function() {
animationFrame = ellipseRun();
clearTimeout(startTimer);
}, 1000);
}`
二、橢圓運動實現
(1)從圖上可以知道每個橢圓大小、傾斜角度均不同,那先得準備三個不同的畫布,適合不同的橢圓運動。在上一章的代碼基礎上加上軌跡canvans的html代碼,如下:
<div class="servicMainOut">
......
<canvas id="homeCanvasBig" style="transform: rotate(90deg);position: absolute;top: -115px; left: 274px;"></canvas>
<canvas id="homeCanvasMid" style="transform: rotate(90deg);position: absolute;top:-139px;left:276px;"></canvas>
<canvas id="homeCanvasSmall" style="transform: rotate(90deg);position: absolute;top:-156px;left: 282px;"></canvas></div>
(2) 調用橢圓運動函數:
// 首頁服務與運維動畫jscircleRunEllipse('homeCanvasBig', 487, 169, 1);
circleRunEllipse('homeCanvasMid', 369, 123, 2);
setTimeout(function() {
circleRunEllipse('homeCanvasSmall', 288, 92, 3);
}, 6014);
編寫這些代碼就可以具體實現了完整的小球沿橢圓運動及到點則閃爍及運動了。
三、實現原理
下面以外圈homeCanvasBig爲例講述製作主要原理。
(1) 軌跡探測
上面的代碼已具體實現了整個C4D效果了。但在開始橢圓運動之前,其實是先要探索匹配好整個橢圓的運動軌跡。開啓測試的匹配軌跡函數drawEllipse(),暫時關閉小球運動drawCircle()以及註釋clearRun(5)以便於調試。
效果如下:
如何匹配橢圓軌跡,一般只要逐步調整以下變量:
ellipseHeight:調整橢圓長袖,橢圓的寬度變化。值越大,圓越大。
ellipseWidth:調整橢圓向北傾斜度。值越大,越傾向北。
一般還搭配top跟left樣式調整。
(2) 起始點、終止點定位
每個橢圓運動都有起始點和終止點,知道這兩點的位置纔能有利於我們控制隱藏不必要的軌跡以及閃爍效果。其實每個橢圓的開始都是有完整的軌跡的,通過drawPoint(x, y)函數定義到每一個點,這樣就可以使用context.clearRect()函數取消不存在的軌跡。譬如:
(3) 閃爍控制
通過上面的起點、終點的定位,我們可以console.log出對應的ellipseTime的值。根據ellipseTime的值則可控制是否應當閃爍。並且ellipseTime增值可控制小球順時針運動,減值則控制逆時針運動。
睿江雲官網鏈接:https://www.eflycloud.com/home?from=RJ0027