2017-09-26 16 views
2

我正在嘗試創建一個超級驅動效果,就像星球大戰中星星有運動軌跡一樣。我已經儘可能在單個圓上創建運動軌跡,它仍然看起來像軌跡在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(); 
} 

回答

4

帆布反饋和顆粒。

這種外匯可以通過多種方式完成。

您可以使用粒子系統並繪製從中心點移開的恆星(如線條),因爲速度會增加您增加的線條長度。當在低速線路變成一個圓,如果你設置ctx.lineWidth > 1ctx.lineCap = "round"

要添加到FX,您可以使用渲染反饋,我認爲你已經超過其自我呈現在畫布上完成的。如果你把它放大一點,你會得到一個縮放效果。如果您使用ctx.globalCompositeOperation = "lighter",則可以在加速時增加恆星強度,以彌補恆星移動速度加快時的整體亮度損失。

我被衝昏頭腦,所以你將不得不通過代碼進行篩選找到你所需要的。

粒子系統使用Point對象和一個名爲bubbleArray的特殊數組來停止掃描動畫的GC命中。

如果需要,您可以只使用普通數組。顆粒獨立於氣泡陣列。當它們移出屏幕時,它們會移動到池中,並在需要新的粒子時再次使用。該update功能將它們移動和draw功能吸引他們,我想大聲笑

功能loop是主循環,並增加了並提請顆粒(我已經設置了粒子數到400,但應該處理更多)

的超級驅動器通過鼠標按鈕進行操作。按下,放開。 (如果正在顯示文字,將會扭曲文字)

畫布反饋通過hyperSpeed變量設置,數學有點複雜。在這種情況下,sCurce函數僅將值限制爲0,1,以使alpha不會超過或低於1.0。 hyperZero只是sCurve返回1,這是超級驅動器最慢的速度。

我推動了非常接近極限的反饋。在loop功能的前幾行,你可以設定最高速度if(mouse.button){ if(hyperSpeed < 1.75){超過這個值1.75,你會開始變得糟糕FX,在約2整個屏幕就會轉到白(我認爲這是在哪裏)

只要玩它,如果你有問題在評論中問。

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

 
// very simple mouse 
 
const mouse = {x : 0, y : 0, button : false} \t 
 
function mouseEvents(e){ 
 
\t mouse.x = e.pageX; 
 
\t mouse.y = e.pageY; 
 
\t 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); 
 
    } 
 
\t requestAnimationFrame(loop); 
 
} 
 
requestAnimationFrame(loop);
canvas { position : absolute; top : 0px; left : 0px; }
<canvas id="canvas"></canvas>

+0

我搜索分鐘,並在那裏找不到科洛桑...這是一個錯誤嗎?真的很好,即使它離OP的代碼很遠(也許是目前的水平?) – Kaiido

+1

@Kaiido謝謝,:)就像我在答案中說的,我被帶走了。 OP應該能夠從中得到一些有用的東西,我一直在代碼中添加註釋,因爲很難用語言來解釋如何實現這些類型的FX。 – Blindman67

+0

謝謝你寫這個。我很欣賞你寫的內容,並希望它能很好地服務於社區。這超出了我的水平,因爲我之前從未使用過畫布。當然,我已經看到了一些第一次畫布嘗試的驚人例子。無論如何,謝謝。我非常簡單地爲觸摸支持進一步開發,我認爲我打破了它,但現在我在同一臺機器上運行這個例子,我得到的錯誤是,默認情況下,從文本到星星的所有內容都有一個動作隨着它進入水平,它將永遠持續下去。 mouse.button是錯誤的,沒有鼠標或觸摸事件觸發。 – wordSmith

0

下面是另一個簡單的例子,主要是基於相同的想法Blindman67,concetric線以不同的速度移動,從中心遠離(距離中心越遠,速度越快其移動..)也這裏沒有回收池。

"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() { 
 
\t var X = []; 
 
\t var Y = []; 
 
\t for(var i = 0; i < stars; i++) { 
 
\t \t var x = Math.random() * w; 
 
\t \t var y = Math.random() * h; 
 
\t \t X.push(translateX(x)); 
 
\t \t Y.push(translateY(y)); 
 
\t } 
 
\t drawTrails(X, Y) 
 
} 
 

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

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

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

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

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

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

 
}
body { 
 
\t overflow: hidden; 
 
} 
 

 
canvas { 
 
\t position: absolute; 
 
\t left: 0; 
 
\t top: 0; 
 
}