在随机放置的圆圈中的画布中的超级驱动效果

时间:2017-09-26 21:39:51

标签: javascript animation canvas

我正在尝试创建一个超级驱动效果,就像星球大战那样,星星有一个运动轨迹。我已经在单个圆圈上创建了运动轨迹,它仍然看起来像是在y方向上向下走,而在z方向上没有向前或正向。

另外,我怎么能用(很多)随机放置的圆圈做这个,好像它们是星星一样?

我的代码位于jsfiddle(https://jsfiddle.net/5m7x5zxu/)及以下位置:

var canvas = document.querySelector("canvas");
var context = canvas.getContext("2d");

var xPos = 180;
var yPos = 100;

var motionTrailLength = 16;
var positions = [];

function storeLastPosition(xPos, yPos) {
  // push an item
  positions.push({
    x: xPos,
    y: yPos
  });

  //get rid of first item
  if (positions.length > motionTrailLength) {
    positions.pop();
  }
}
function update() {
  context.clearRect(0, 0, canvas.width, canvas.height);

  for (var i = positions.length-1; i > 0; i--) {
    var ratio = (i - 1) / positions.length;
    drawCircle(positions[i].x, positions[i].y, ratio);
  }

  drawCircle(xPos, yPos, "source");
 var k=2;
  storeLastPosition(xPos, yPos);

  // update position
  if (yPos > 125) {
    positions.pop();
  }
  else{
    yPos += k*1.1;
   }

  requestAnimationFrame(update);
}
update();


function drawCircle(x, y, r) {
  if (r == "source") {
    r = 1;
  } else {
    r*=1.1;
  }

  context.beginPath();
  context.arc(x, y, 3, 0, 2 * Math.PI, true);
  context.fillStyle = "rgba(255, 255, 255, " + parseFloat(1-r) + ")";
  context.fill();
}

2 个答案:

答案 0 :(得分:6)

画布反馈和粒子。

这种FX可以通过多种方式完成。

你可以使用粒子系统并绘制远离中心点的星星(作为线条),随着速度的增加你增加线条长度。如果设置ctx.lineWidth > 1ctx.lineCap = "round"

,则在低速时线条会变成圆形

要添加到FX,您可以使用渲染反馈,因为我认为您已经完成了将画布渲染到自身上。如果你渲染得稍大,你会得到一个变焦效果。如果使用ctx.globalCompositeOperation = "lighter",你可以在加速时增加星星强度,以便在星星移动得更快时弥补亮度的整体损失。

实施例

我被带走了所以你必须筛选代码以找到你需要的东西。

粒子系统使用Point对象和一个名为bubbleArray的特殊数组来阻止GC点击动画。

如果需要,您只能使用普通数组。颗粒独立于气泡阵列。当他们移出屏幕时,他们会移动到池中,并在需要新粒子时再次使用。 update函数移动它们,draw函数绘制它们我猜LOL

函数loop是主循环并添加和绘制粒子(我已将粒子数设置为400,但应该处理更多)

通过鼠标按钮操作超级驱动器。按下打开,放开。 (如果正在显示文本,它会扭曲文本)

画布反馈是通过hyperSpeed变量设置的,数学有点复杂。在这种情况下,sCurce函数仅将值限制为0,1,以阻止alpha超过或低于1,0。 hyperZero只是sCurve返回1,这是超级驱动最慢的速度。

我已将反馈非常接近极限。在loop函数的前几行中,您可以设置最高速度if(mouse.button){ if(hyperSpeed < 1.75){超过此值1.75并且您将开始变坏FX,大约2整个屏幕将变为白色(我认为那是哪里)

只要玩它就可以在评论中提问。

&#13;
&#13;
const ctx = canvas.getContext("2d");

// very simple mouse
const mouse  = {x : 0, y : 0, button : false}	
function mouseEvents(e){
	mouse.x = e.pageX;
	mouse.y = e.pageY;
	mouse.button = e.type === "mousedown" ? true : e.type === "mouseup" ? false : mouse.button;
}
["down","up","move"].forEach(name => document.addEventListener("mouse"+name,mouseEvents));

// High performance array pool using buubleArray to seperate pool objects and active object.
// This is designed to eliminate GC hits involved with particle systems and 
// objects that have short liftimes but used often.
// Warning this code is not well tested.
const bubbleArray = () => {
    const items = [];
    var count = 0;
    return {
        clear(){  // warning this dereferences all locally held references and can incur Big GC hit. Use it wisely.
            this.items.length = 0;
            count = 0;
        },
        update() {
            var head, tail;
            head = tail = 0;
            while(head < count){
                if(items[head].update() === false) {head += 1 }
                else{
                    if(tail < head){
                        const temp = items[head];
                        items[head] = items[tail];
                        items[tail] = temp;
                    }
                    head += 1;
                    tail += 1;
                }
            }
            return count = tail;
        },
        createCallFunction(name, earlyExit = false){
            name = name.split(" ")[0];
            const keys = Object.keys(this);
            if(Object.keys(this).indexOf(name) > -1){  throw new Error(`Can not create function name '${name}' as it already exists.`) }
            if(!/\W/g.test(name)){
                let func;
                if(earlyExit){
                    func = `var items = this.items; var count = this.getCount(); var i = 0;\nwhile(i < count){ if (items[i++].${name}() === true) { break } }`;
                }else{
                    func = `var items = this.items; var count = this.getCount(); var i = 0;\nwhile(i < count){ items[i++].${name}() }`;
                }
                !this.items && (this.items = items);
                this[name] = new Function(func);
            }else{  throw new Error(`Function name '${name}' contains illegal characters. Use alpha numeric characters.`) }
            
        },
        callEach(name){var i = 0; while(i < count){ if (items[i++][name]() === true) { break } } },
        each(cb) { var i = 0; while(i < count){ if (cb(items[i], i++) === true) { break } } },
        next() { if (count < items.length) { return items[count ++] } },
        add(item) {
            if(count === items.length){
                items.push(item);
                count ++;
            }else{
                items.push(items[count]);
                items[count++] = item;
            }
            return item;
        },
        getCount() { return count },
    }
}

// Helpers rand float, randI random Int
// doFor iterator
// sCurve curve input -Infinity to Infinty out -1 to 1
// randHSLA creates random colour
// CImage, CImageCtx create image and image with context attached
const randI = (min, max = min + (min = 0)) => (Math.random() * (max - min) + min) | 0;
const rand  = (min = 1, max = min + (min = 0)) => Math.random() * (max - min) + min;
const doFor = (count, cb) => { var i = 0; while (i < count && cb(i++) !== true); }; // the ; after while loop is important don't remove

const sCurve = (v,p) => (2 / (1 + Math.pow(p,-v))) -1;
const randHSLA = (h, h1, s = 100, s1 = 100, l = 50, l1 = 50, a = 1, a1 = 1) => { return `hsla(${randI(h,h1) % 360},${randI(s,s1)}%,${randI(l,l1)}%,${rand(a,a1)})` }
const CImage = (w = 128, h = w) => (c = document.createElement("canvas"),c.width = w,c.height = h, c);
const CImageCtx = (w = 128, h = w) => (c = CImage(w,h), c.ctx = c.getContext("2d"), c);

// create image to hold text
var textImage = CImageCtx(512,512);
var c = textImage.ctx;
c.fillStyle = "#FF0";
c.font = "32px arial black";
c.textAlign = "center";
c.textBaseline = "middle";
const text = "HYPER,SPEED,V,,Hold the mouse,button to increase,speed.".split(",");
text.forEach((line,i) => { c.fillText(line,256,i * 34 + 34) });

function starWarIntro(image,x1,y1,x2,y2,pos){
  var iw = image.width;
  var ih = image.height;
  var hh = (x2 - x1) / (y2 - y1);      // Slope of left edge
  var w2 = iw / 2;                      // half width
  var z1 = w2 - x1;                    // Distance (z) to first line
  var z2 = (z1 / (w2 - x2)) * z1 - z1; // distance (z) between first and last line
  var sk,t3,t3a,z3a,lines, z3, dd;
  for (var y = y1; y < y2; y++) {      // for each line

      t3 = ((y - y1) * hh) + x1;       // get scan line top left edge
      t3a = (((y+1) - y1) * hh) + x1;  // get scan line bottom left edge
      z3 = (z1 / (w2 - t3)) * z1;      // get Z distance to top of this line
      z3a = (z1 / (w2 - t3a)) * z1;      // get Z distance to bottom of this line
      dd = ((z3 - z1) / z2) * ih;       // get y bitmap coord
      ctx.globalAlpha = (dd / ih);
      dd += pos;                         // cludge for this answer to make text move
                                         // does not move text correctly 
      lines = ((z3a - z1) / z2) * ih-dd;       // get number of lines to copy
      ctx.drawImage(image, 0, dd , iw, lines, t3, y, w - t3 * 2, 1);
  }
}


// canvas settings
var w = canvas.width;
var h = canvas.height;
var cw = w / 2;  // center 
var ch = h / 2;
// diagonal distance used to set point alpha (see point update)
var diag = Math.sqrt(w * w + h * h);
// If window size is changed this is called to resize the canvas
// It is not called via the resize event as that can fire to often and
// debounce makes it feel sluggish so is called from main loop.
function resizeCanvas(){
  points.clear();
  canvas.width = innerWidth;
  canvas.height = innerHeight;
  w = canvas.width;
  h = canvas.height;
  cw = w / 2;  // center 
  ch = h / 2;
  diag = Math.sqrt(w * w + h * h);
  
}
// create array of points
const points = bubbleArray(); 
// create optimised draw function itterator
points.createCallFunction("draw",false);
// spawns a new star
function spawnPoint(pos){
    var p = points.next();
    p = points.add(new  Point())    
    if (p === undefined) { p = points.add(new  Point()) }
    p.reset(pos);  
}
// point object represents a single star
function Point(pos){  // this function is duplicated as reset 
    if(pos){
        this.x = pos.x;   
        this.y = pos.y;   
        this.dead = false;
    }else{
        this.x = 0;
        this.y = 0;
        this.dead = true;
    }
    this.alpha = 0;
    var x = this.x - cw;
    var y = this.y - ch;
    this.dir = Math.atan2(y,x);
    this.distStart = Math.sqrt(x * x + y * y);
    this.speed = rand(0.01,1);
    this.col = randHSLA(220,280,100,100,50,100);
    this.dx = Math.cos(this.dir) * this.speed;
    this.dy = Math.sin(this.dir) * this.speed;
}
Point.prototype = {
    reset : Point,  // resets the point
    update(){       // moves point and returns false when outside 
        this.speed *= hyperSpeed;  // increase speed the more it has moved
        this.x += Math.cos(this.dir) * this.speed;
        this.y += Math.sin(this.dir) * this.speed;
        var x = this.x - cw;
        var y = this.y - ch;
        this.alpha = (Math.sqrt(x * x + y * y) - this.distStart) / (diag * 0.5 - this.distStart);
        if(this.alpha > 1 || this.x < 0 || this.y < 0 || this.x > w || this.h > h){
           this.dead = true;
        }
        return !this.dead;
    },
    draw(){  // draws the point 
        ctx.strokeStyle = this.col;
        ctx.globalAlpha = 0.25 + this.alpha *0.75;
        ctx.beginPath();
        ctx.lineTo(this.x - this.dx * this.speed, this.y - this.dy * this.speed);
        ctx.lineTo(this.x, this.y);
        ctx.stroke();

    }
}

const maxStarCount = 400;
const p = {x : 0, y : 0};
var hyperSpeed = 1.001;
const alphaZero = sCurve(1,2);
var startTime;
function loop(time){

    if(startTime === undefined){
        startTime = time;
    }
    if(w !== innerWidth || h !== innerHeight){
       resizeCanvas();
    }
    // if mouse down then go to hyper speed
    if(mouse.button){
        if(hyperSpeed < 1.75){
            hyperSpeed += 0.01;
        }
    }else{
        if(hyperSpeed > 1.01){
            hyperSpeed -= 0.01;
        }else if(hyperSpeed > 1.001){
            hyperSpeed -= 0.001;
        }
    }
    
    var hs = sCurve(hyperSpeed,2);
    ctx.globalAlpha = 1;
    ctx.setTransform(1,0,0,1,0,0); // reset transform


    //==============================================================
    // UPDATE the line below could be the problem. Remove it and try
    // what is under that        
    //==============================================================
    //ctx.fillStyle = `rgba(0,0,0,${1-(hs-alphaZero)*2})`;
    
    // next two lines are the replacement
    ctx.fillStyle = "Black";
    ctx.globalAlpha = 1-(hs-alphaZero) * 2;
    //==============================================================



    ctx.fillRect(0,0,w,h);
    // the amount to expand canvas feedback
    var sx = (hyperSpeed-1) * cw * 0.1;
    var sy = (hyperSpeed-1) * ch * 0.1;

    // increase alpha as speed increases
    ctx.globalAlpha = (hs-alphaZero)*2;
    ctx.globalCompositeOperation = "lighter";
    // draws feedback twice
    ctx.drawImage(canvas,-sx, -sy, w + sx*2 , h + sy*2)
    ctx.drawImage(canvas,-sx/2, -sy/2, w + sx , h + sy)
    ctx.globalCompositeOperation = "source-over";
    
    // add stars if count < maxStarCount 
    if(points.getCount() < maxStarCount){
        var cent = (hyperSpeed - 1) *0.5; // pulls stars to center as speed increases
        doFor(10,()=>{
            p.x = rand(cw * cent ,w - cw * cent);  // random screen position
            p.y = rand(ch * cent,h - ch * cent);
            spawnPoint(p)
            
        })
    }
    // as speed increases make lines thicker
    ctx.lineWidth = 2 + hs*2;
    ctx.lineCap = "round";
    points.update();  // update points
    points.draw();     // draw points
    ctx.globalAlpha = 1;

    // scroll the perspective star wars text FX
    var scrollTime = (time - startTime) / 10 - 1212;
    if(scrollTime < 512){
        starWarIntro(textImage,cw - h * 0.5, h * 0.2, cw - h * 3, h , scrollTime );
    }
	requestAnimationFrame(loop);
}
requestAnimationFrame(loop);
&#13;
canvas { position : absolute; top : 0px; left : 0px; }
&#13;
<canvas id="canvas"></canvas>
&#13;
&#13;
&#13;

答案 1 :(得分:0)

这是另一个简单的例子,主要基于与Blindman67相同的想法,以不同的速度远离中心的中心线(离中心越远,移动的速度越快......)这里也没有回收池。 / p>

&#13;
&#13;
 "use strict"
var c = document.createElement("canvas");
document.body.append(c);
var ctx = c.getContext("2d");
var w = window.innerWidth;
var h = window.innerHeight;
var ox = w / 2;
var oy = h / 2;
c.width = w; c.height = h;

const stars = 120;
const speed = 0.5;
const trailLength = 90; 

ctx.fillStyle = "#000";
ctx.fillRect(0, 0, w, h);
ctx.fillStyle = "#fff"
ctx.fillRect(ox, oy, 1, 1);

init();

function init() {
	var X = [];
	var Y = [];
	for(var i = 0; i < stars; i++) {
		var x = Math.random() * w;
		var y = Math.random() * h;
		X.push( translateX(x) );
		Y.push( translateY(y) );
	}
	drawTrails(X, Y)
}

function translateX(x) {
	return x - ox;
}

function translateY(y) {
	return oy - y;
}

function getDistance(x, y) {
	return Math.sqrt(x * x + y * y);
}

function getLineEquation(x, y) {
	return function(n) {
			return y / x * n;
		}
}

function drawTrails(X, Y) {
	var count = 1;
	ctx.fillStyle = "#000";
	ctx.fillRect(0, 0, w, h);
	function anim() {
		for(var i = 0; i < X.length; i++) {
			var x = X[i];
			var y = Y[i];
			drawNextPoint(x, y, count);
		}
		count+= speed;
		if(count < trailLength) {
			window.requestAnimationFrame(anim);
		}
		else {
			init();
		}
	}
	anim();
}

function drawNextPoint(x, y, step) {
	ctx.fillStyle = "#fff";
	var f = getLineEquation(x, y);
	var coef = Math.abs(x) / 100;
	var dist = getDistance( x, y);
	var sp = speed * dist / 100;
	for(var i = 0; i < sp; i++) {
		var newX = x + Math.sign(x) * (step + i) * coef;
		var newY = translateY( f(newX) );
		ctx.fillRect(newX + ox, newY, 1, 1);
	}

}
&#13;
body {
	overflow: hidden;
}

canvas {
	position: absolute;
	left: 0;
	top: 0;
}
&#13;
&#13;
&#13;