我有一个案例,我想绘制3条弧线并擦除它们。
应逐步绘制第一个Arc CA,然后逐步删除它。然后应该绘制并擦除弧AB,然后弧BC也应该这样做。然后重复一遍。
我的方法:
使用canvas和JS:
我从画布开始,但抗锯齿在这里没有效果。所以我想可能是SVG会更好。
var currentEndAngle = 0;
var currentStartAngle = 0;
var currentColor = 'black';
var lineRadius = 300;
var lineWidth = 5;
setInterval(draw, 5);
function draw() {
var can = document.getElementById('canvas1'); // GET LE CANVAS
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius;
var width;
var startAngle = currentStartAngle * Math.PI;
var endAngle = (currentEndAngle) * Math.PI;
currentStartAngle = currentEndAngle - 0.01;
currentEndAngle = currentEndAngle + 0.01;
if (Math.floor(currentStartAngle / 2) % 2) {
currentColor = "white";
radius = lineRadius - 1;
width = lineWidth + 3;
} else {
currentColor = "black";
radius = lineRadius;
width = lineWidth;
}
var counterClockwise = false;
context.beginPath();
context.arc(x, y, radius, startAngle, endAngle, counterClockwise);
context.lineWidth = width;
// line color
context.strokeStyle = currentColor;
context.stroke();
/************************************************/
}
body {
text-align: center;
background: blue;
}
#canvas1 {
width: 500px;
height: 500px;
margin: 0 auto;
}
<canvas id="canvas1" width="700" height="700"></canvas>
使用SVG和CSS
SVG方法看起来更顺畅。但我不明白如何修改dasharray,dashoffset和圆的半径以获得3个弧形动画。
circle {
fill: transparent;
stroke: black;
stroke-width: 2;
stroke-dasharray: 250;
stroke-dashoffset: 0;
animation: rotate 5s linear infinite;
}
@keyframes rotate {
0% {
stroke-dashoffset: 500;
}
100% {
stroke-dashoffset: 0;
}
}
<svg height="400" width="400">
<circle cx="100" cy="100" r="40" />
</svg>
那么,如果有人可以帮我扩展代码或指导如何从svg圈创建三个弧以及如何设置dasharray,dashoffset和radius?
如果您有更好的解决方案,那么上述2方法请告诉我。
我也曾尝试使用GSAP的drawingvg插件,我想这可能会更容易,但我不允许在我的项目中使用'drawnvg'插件。
答案 0 :(得分:3)
你真的不想修改stroke-dashoffset
,因为这只会在圆圈周围移动短划线。
您还必须修改破折号数组值,因此您可以通过设置破折号数组中的值来完成所有操作。
你的圆的半径为40,所以圆周为251.33。这意味着你的三个弧的长度都是83.78。
对于这三个阶段中的每个阶段,我们都会在&#34;&#34;破折号的一部分从0到83.78。然后我们再次缩减它,同时将先前的差距从83.78增加到167.55。这样尾巴就会被推到最后。
这适用于前两个步骤,但由于短划线模式在3点钟位置开始和结束(并且没有包围该点),我们必须进行尾推最后一步,在开始时使用一个额外的空虚线对。我们将该差距从0增加到83.78。
circle {
fill: transparent;
stroke: black;
stroke-width: 2;
animation: rotate 5s linear infinite;
}
@keyframes rotate {
0% { stroke-dasharray: 0 0 0 83.78 0 83.78 0 83.78; }
16.7% { stroke-dasharray: 0 0 0 83.78 83.78 0 0 83.78; }
33.3% { stroke-dasharray: 0 0 0 167.55 0 0 0 83.78; }
50% { stroke-dasharray: 0 0 0 83.78 0 83.78 83.78 0; }
66.6% { stroke-dasharray: 0 0 0 83.78 0 167.55 0 0; }
83.3% { stroke-dasharray: 0 0 83.78 0 0 83.78 0 83.78; }
100% { stroke-dasharray: 0 83.78 0 0 0 83.78 0 83.78; }
}
&#13;
<svg height="400" width="400">
<circle cx="100" cy="100" r="40" />
</svg>
&#13;
答案 1 :(得分:2)
对于画布版本,如评论中所述,您的抗锯齿问题是您在相同的像素上反复重绘。
为避免这种情况,请每帧清除整个画布并重新绘制所有内容。
对于您要求的动画,您必须同时存储起始角度和结束角度。然后你会一个接一个地递增,当你超过分割大小阈值时进行切换。
这是一个带注释的代码片段,可以让我希望更清楚。
// settings
var divisions = 3;
var duration = 3000; // in ms
var canvas = document.getElementById("canvas1");
var context = canvas.getContext("2d");
var x = canvas.width / 2;
var y = canvas.height / 2;
var radius = (canvas.width / 7) * 2;
context.lineWidth = 4;
// init
var currentSplit = 0;
var splitAngle = (Math.PI * 2) / divisions;
var splitTime = (duration / (divisions*2)); // how much time per split per end
var angles = [0,0]; // here we store both start and end angle
var current = 0;
var startTime = performance.now();
draw();
function draw(currentTime) {
// first convert the elapsed time to an angle
var timedAngle = ((currentTime - startTime) / splitTime) * splitAngle;
// set the current end to this timed angle + the current position on the circle
angles[current] = timedAngle + (splitAngle * currentSplit);
if (timedAngle >= splitAngle) { // one split is done for this end
// it should not go farther than the threshold
angles[current] = (splitAngle * (currentSplit + 1));
current = +(!current) // switch which end should move
startTime = currentTime; // reset the timer
if(!current){ // we go back to the start
currentSplit = (currentSplit + 1) % divisions; // increment our split index
}
}
if(angles[1] > Math.PI*2){ // we finished one complete revolution
angles[0] = angles[1] = current = 0; // reset everything
}
// at every frame we clear everything
context.clearRect(0, 0, canvas.width, canvas.height);
// and redraw
context.beginPath();
context.arc(x, y, radius, angles[0], angles[1], true);
context.stroke();
requestAnimationFrame(draw); // loop at screen refresh rate
}
body {
text-align: center;
}
#canvas1 {
width: 250px;
height: 150px;
}
<canvas id="canvas1" width="500" height="300"></canvas>
答案 2 :(得分:1)
Canvas,(或CSS,HTML,SVG)与javascript相结合,总是胜过CSS,SVG,HTML,因为Javascript更具适应性。 HTML,CSS和SVG是声明性语言,而JavaScript是一种功能齐全的命令式语言,可以执行任何其他编程语言所能做的任何事情。
使用javascript添加到HTML,CSS,SVG功能,有效地声明这些语言的新行为。
一旦定义了Javascript功能,就可以忘记javascript,并根据需要使用HTML,CSS或SVG调用新行为。
在这种情况下,所有具有类名"segmentedProgress"
的元素都将成为动画进度。您可以根据需要设置任意数量的属性来控制行为并将其添加到元素的数据属性中。
例如
<div class="segmentedProgress"></div>
<!-- showing defaults as above element will be setup -->
<div class="segmentedProgress"
data-angle-steps = 3 <!-- number of segments. (integers only) -->
data-speed = 1000 <!-- Time per segment in ms -->
data-easing = "1.2" <!-- easing power -->
data-line-width = "0.1" <!-- as fraction of radius -->
data-radial-size = "0.33" <!-- as fraction of shortest dimension -->
data-color = "black" <!-- colour of line -->
></div>
只要包含Javascript,就会在页面上自动显示正确配置的每个元素的进度。如果您的服务器设置可以识别页面内容依赖性,那么您需要做的就是将行为添加到页面,因为服务器将添加使其运行所需的内容。
实现并不需要太多的javascript。您将找到具有相应类名的所有元素,并将它们添加到进度项数组中。然后根据需要制作动画。
document.addEventListener("load", function(){
var elements = [...document.body.querySelectorAll(".segmentedProgress")];
if(elements.length === 0){ // exit if nothing found
return;
}
// singleton to isolate from onload
(function(){
const error = 0.01; // Math too perfect causes zero len arc to draw nothing. Error makes sure there is always some length in the drawn arc
const items = []; // array of progress items
// each progress item defaults
var defaults = {
angleSteps : 3, // number of segments. (integers only)
speed : 1000, // Time per segment in ms
easing : 1.2, // easing power where 1 = no easing 2 = normal quadratic easing 1/2= inverse quadratic easing
lineWidth : 0.1, // as fraction of radius
radialSize : 0.33,// as fraction of shortest dimension
color : "black", // colour of line
complete : false, // not used
resize () { // resize the canvas and set size dependent vars
this.bounds = this.element.getBoundingClientRect();
this.w = this.canvas.width = this.bounds.width;
this.h = this.canvas.height = this.bounds.height;
this.canvas.style.top = (this.bounds.top + scrollY) + "px";
this.canvas.style.left = (this.bounds.left + scrollX) + "px";
this.pos = { x : this.w / 2, y : this.h / 2}; // position of circle
this.radius = Math.min(this.w, this.h) * this.radialSize; // radius of circle
// set canvas state constants
this.ctx.lineCap = "round";
},
update (time) { // updates and renders
var segStart, segProgress, pp, ctx, ang;
ctx = this.ctx; // alias to this.ctx
// clear the canvas
ctx.clearRect(0, 0, this.w, this.h);
// get current selment angle
ang = Math.PI * 2 / this.angleSteps, // Radians per segment
// set the time at the correct speed
time /= this.speed;
// get the segment start position in radians
segStart = Math.floor(time % this.angleSteps) * ang;
// get the unit progress of this stage doubled for grow and shrink stages
var segProgress = (time % 1) * 2;
var pp = segProgress % 1; // pp partial progress
pp = (pp ** this.easing) / ((pp ** this.easing) + (1 - pp) ** this.easing); // add some easing
ctx.beginPath();
// first half of progress is growth
if(segProgress <= 1){
ctx.arc(this.pos.x, this.pos.y, this.radius, segStart, segStart + pp * ang + error);
}else{
// second half of progress is shrink
ctx.arc(this.pos.x, this.pos.y, this.radius, segStart + pp * ang - error, segStart + ang);
}
ctx.strokeStyle = this.color;
ctx.lineWidth = this.radius * this.lineWidth;
ctx.stroke();
}
}
// create prgress item for each found element
elements.forEach(element => {
var pItem = {...defaults}; // progress item
pItem.element = element;
// get any element setting that overwrite the defaults
Object.keys(defaults).forEach(key => {
if(typeof defaults[key] !== "function"){
if(element.dataset[key] !== undefined){
pItem[key] = element.dataset[key];
if(! isNaN(element.dataset[key])){
pItem[key] = Number(pItem[key]);
}
}
}
});
pItem.canvas = document.createElement("canvas");
pItem.ctx = pItem.canvas.getContext("2d");
pItem.canvas.style.position = "absolute";
pItem.resize();
items.push(pItem);
element.appendChild(pItem.canvas);
});
elements.length = 0; // let go of elements
// change size on resize
window.addEventListener("resize", () =>{
items.forEach(pItem => pItem.resize());
});
// start the animation
requestAnimationFrame(update);
// main update loop
function update (time) {
items.forEach(pItem => {
pItem.update(time);
});
requestAnimationFrame(update);
}
}());
}());
//document.addEventListener("load",()=>{
;(function(){
var elements = [...document.body.querySelectorAll(".segmentedProgress")];
if (elements.length === 0) { return }
(function () {
const error = 0.001; // Math too perfect causes zero len arc to draw nothing. Error makes sure there is always some length in the drawn arc
const items = []; // array of progress items
var defaults = {
angleSteps : 3, // number of segments. (integers only)
speed : 1000, // Time per segment in ms
easing : 1.2, // easing power where 1 = no easing 2 = normal quadratic easing 1/2= inverse quadratic easing
lineWidth : 0.1, // as fraction of radius
radialSize : 0.33,// as fraction of shortest dimension
color : "black", // colour of line
complete : false, // not used
resize () { // resize the canvas and set size dependent vars
this.bounds = this.element.getBoundingClientRect();
this.w = this.canvas.width = this.bounds.width;
this.h = this.canvas.height = this.bounds.height;
this.canvas.style.top = (this.bounds.top + scrollY) + "px";
this.canvas.style.left = (this.bounds.left + scrollX) + "px";
this.pos = { x : this.w / 2, y : this.h / 2}; // position of circle
this.radius = Math.min(this.w, this.h) * this.radialSize; // radius of circle
this.ctx.lineCap = "round";
},
update (time) { // updates and renders
var segStart, segProgress, pp, ctx, ang;
ctx = this.ctx; // alias to this.ctx
ctx.clearRect(0, 0, this.w, this.h);
ang = Math.PI * 2 / this.angleSteps, // Radians per segment
time /= this.speed;
segStart = Math.floor(time % this.angleSteps) * ang;
var segProgress = (time % 1) * 2;
var pp = segProgress % 1; // pp partial progress
// babel can not handle the following line even though most
// browsers can
// pp = (pp ** this.easing) / ((pp ** this.easing) + (1 - pp) ** this.easing); // add some easing
// to cover babel error
pp = Math.pow(pp,this.easing) / (Math.pow(pp,this.easing) + Math.pow(1 - pp, this.easing)); // add some easing
ctx.beginPath();
if(segProgress <= 1){
ctx.arc(this.pos.x, this.pos.y, this.radius, segStart, segStart + pp * ang + error);
}else{
ctx.arc(this.pos.x, this.pos.y, this.radius, segStart + pp * ang - error, segStart + ang);
}
ctx.strokeStyle = this.color;
ctx.lineWidth = this.radius * this.lineWidth;
ctx.stroke();
}
}
elements.forEach(element => {
var pItem = {...defaults}; // progress item
pItem.element = element;
Object.keys(defaults).forEach(key => {
if(typeof defaults[key] !== "function"){
if(element.dataset[key] !== undefined){
pItem[key] = element.dataset[key];
if(! isNaN(element.dataset[key])){
pItem[key] = Number(pItem[key]);
}
}
}
});
pItem.canvas = document.createElement("canvas");
pItem.ctx = pItem.canvas.getContext("2d");
pItem.canvas.style.position = "absolute";
pItem.resize();
items.push(pItem);
element.appendChild(pItem.canvas);
});
elements.length = 0;
window.addEventListener("resize", () =>{ items.forEach(pItem => pItem.resize()) });
requestAnimationFrame(update);
function update (time) {
items.forEach(pItem => { pItem.update(time) });
requestAnimationFrame(update);
}
}());
}());
.segmentedProgress {
width : 100px;
height : 100px;
}
.big {
width : 200px;
height : 200px;
}
.large {
width : 512px;
height : 512px;
background : #4AF;
}
4 segment fast.
<div class="segmentedProgress" data-color="red" data-speed ="250" data-line-width="0.3" data-angle-steps=4 ></div>
Default Progress
<div class="segmentedProgress" ></div>
Big progress
<div class="big segmentedProgress" data-color="blue" data-speed ="2500" data-line-width="0.3" data-angle-steps=2 ></div>
60 Seconds two overlap
<div class="large segmentedProgress" data-color="white" data-speed ="1000" data-line-width="0.02" data-angle-steps=60 >
<div class="large segmentedProgress" data-color="white" data-speed ="1000" data-line-width="0.02" data-angle-steps=2 data-radial-size = "0.34">
</div>