1个canvas互动,3个展示

One canvas to interact, three to display

我正在创建一个浏览器游戏,它可以作为全息图来玩。 屏幕应该显示如下内容: https://www.youtube.com/watch?v=Y60mfBvXCj8

因此我认为我必须创建 4 个 canvas(没问题),但其中三个应该只显示第一个发生的情况。 我试图让它绘制 canvas 的图像并让它显示给另一个 canvas。

如有任何帮助,我们将不胜感激!

游戏是用Box2D制作的。

编辑:

我希望 space 船在每个 canvas 中绘制,但只控制在一个中。

我的代码:http://s000.tinyupload.com/index.php?file_id=68837773176112789787

问题是,它只显示在一个 canvas!

我在 HTML 中输入的内容:

<canvas id="canvas1" width="500" height="500"></canvas>
<canvas id="canvas2"  width="500" height="500"></canvas>
<canvas id="canvas3"  width="500" height="500"></canvas>
<canvas id="canvas4"  width="500" height="500"></canvas>

打印给其他人的意义:

JS

var sourceCtx, destinationCtx, imageData;

//get the context of each canvas
sourceCtx = canvas2.getContext('2d');
canvas2Ctx = canvas3.getContext('2d');

//copy the data
imageData = sourceCtx.getImageData(0, 0, canvas2.width - 1, canvas2.height - 1);

//apply the image data
canvas3Ctx.putImageData(imageData, 0, 0);

//done

全息金字塔显示器

如何渲染金字塔反射显示器。

为此,请使用 HTML 中的单个显示器 canvas 和存储在内存中的 canvas 用于渲染。

镜像渲染 canvas

渲染 canvas 被剪裁成三角形以防止像素重叠,并且变换被镜像以便正确看到最终效果。例如文本回到前面。

然后将离屏渲染 canvas 渲染到显示器 canvas,从顶部开始,总共制作 4 个副本,每个副本旋转 90 度。

渲染 canvas 宽度将是显示宽度或高度的最小值加上高度的一半以适合显示。

需要全屏模式

要使 FX 正常工作,您需要进入全屏模式。我没有包括这是如何完成的,但我确信在 Whosebug 上有一个 QA 将引导你完成这个过程。

盲区

显示器的中心是金字塔将停留的区域(我称之为死区)由于这些显示器中的许多都是自制的,因此死区的大小会有所不同。在下面的演示的第一行是一个常量 deadZoneSize,它将设置死区大小。当前设置为 0.1,即视图大小的 10%。您可能需要调整此值以适合您的特定反射显示器。

示例代码

代码示例在相关部分充满注释。它将创建和设置显示 canvas 和渲染 canvas。创建剪辑区域并设置镜像渲染变换,以便您可以正常渲染。 mainLoop 函数将调用名为 renderContent 的函数,第一个参数作为渲染的上下文 canvas。只需正常渲染您的内容(使用 sizehSize 作为可见渲染区域的宽度和高度(也许我应该使用更好的名称))

该演示包括一个示例渲染,只是为了好玩,它都在底部,并且评论最少,因为与问题无关。

const deadZoneSize = 0.1; // As fraction of fitted box size

// for FX em and em4 are just custom unit size and 1/4 size
var em,em4;
// to fit all four views use the min width or height
var size = Math.min(innerWidth,innerHeight);
// half size
var hSize = size / 2 | 0;
// there is a small area where nothing should be displayed.
// This will depend on the pyrimide being used.
var deadZone = size * 0.1 | 0; // about 10% of view area

// Display canvas d for display
const dCanvas = document.createElement("canvas");

// Render canvas
const rCanvas = document.createElement("canvas");

// get rendering context for both
const dCtx = dCanvas.getContext("2d");
const rCtx = rCanvas.getContext("2d");

// Set the display canvas to fill the page
Object.assign(dCanvas.style,{
    position : "absolute",
    zIndex : 10, // place above 
    top : "0px",
    left : "0px",
    background : "black",
})

// add the display canvas to the DOM
document.body.appendChild(dCanvas);


//Size function resizes canvases when needed
function resize(){
    startTime = undefined;
    size = Math.min(innerWidth,innerHeight);
    hSize = size / 2 | 0;
    deadZone = size * deadZoneSize | 0; // about 10% of view area
    dCanvas.width = innerWidth;
    dCanvas.height = innerHeight;
    rCanvas.width = size; 
    rCanvas.height = hSize;  // half height
    em = size * 0.1 | 0; // define our own unit size
    em4 = Math.max(1,em * 0.25 | 0); // define quarter unit size min of 1    
}

// To ensure pixels do not stray outside the view area and overlap use a clip on the render canvas
// ctx the context to appy the clip path to
function defineClip(ctx){
    ctx.beginPath();
    ctx.lineTo(0,0);
    ctx.lineTo(size,0);
    ctx.lineTo(hSize + deadZone, hSize - deadZone);
    ctx.lineTo(hSize - deadZone, hSize - deadZone);
    ctx.clip();
    
    // The rendering is mirrored from the holo pyramid
    // to avoid seeing text mirrored you need to mirror the
    // rendering transform
    
    ctx.setTransform(-1,0,0,1,size,0); // x axis from right to left, origin at top right  
}

// Copying the rendered canvas to the display canvas
// ctx is the display canvas context
// image is the rendered canvas
function display(ctx,image) {
    // for each face of the pyramid render a view
    // Each image is just rotated 90 deg
    
    // first clear the canvas
    ctx.clearRect(0,0,ctx.canvas.width, ctx.canvas.height);
    
    // top
    // use the center of the display canvas as the origin
    ctx.setTransform(1,0,0,1,ctx.canvas.width / 2 | 0, ctx.canvas.height / 2 | 0);
    // draw the image
    ctx.drawImage(image,-hSize,-hSize);

    // Right
    ctx.transform(0,1,-1,0,0,0);  // rotate 90 deg. This is better than ctx.rotate as it can have slight
                                  // problems due to floating point errors if not done correctly
    ctx.drawImage(image,-hSize,-hSize);
    // bottom
    ctx.transform(0,1,-1,0,0,0);
    ctx.drawImage(image,-hSize,-hSize);
    // left
    ctx.transform(0,1,-1,0,0,0);
    ctx.drawImage(image,-hSize,-hSize);
    
    // restore the default transform;
    ctx.setTransform(1,0,0,1,0,0);

}



// the main render loop 
var globalTime;
var startTime;
function mainLoop(time){
    // check canvas size. If not matching page then resize
    if(dCanvas.width !== innerWidth || dCanvas.height !== innerHeight) {
        resize();
    }
    if(startTime === undefined){ startTime = time }
    globalTime = time - startTime;
    
    // clear the render canvas ready for next render
    rCtx.setTransform(1,0,0,1,0,0); // reset transform
    rCtx.globalAlpha = 1;           // reset alpha
    rCtx.clearRect(0,0,size,hSize);
    
    // save the context state so that the clip can be removed
    rCtx.save();   
    defineClip(rCtx); // set the clip
    renderContent(rCtx); // call the rendering function

    // restore the context state which removes the clip
    rCtx.restore();
    
    // rendering is ready for display so render the holo view
    // on to the display canvas's context
    display(dCtx, rCanvas);


    requestAnimationFrame(mainLoop);

}
requestAnimationFrame(mainLoop);




//=====================================================================================================
// The following is just something interesting to display and is not directly related to the answer
//=====================================================================================================


// The main rendering function 
// This is where you render your content. It can be anything from a game to just plain old text
// You can even use a video element and display a video.
// The rendering context is already set up to correctly mirror the content so just render everything as normal

const randG  = (min, max , p = 2) => (max + min) / 2 + (Math.pow(Math.random(), p) * (max - min) * 0.5) * (Math.random() < 0.5 ? 1 : -1);

const bootUp = ["Power On",1,1000,"Sub system test",0.5, 3000, "Calibrating scanner",0.5, 6000, "Welcome",1,8000];
function noisyText(ctx){
    var textTime = globalTime / 8000; // 8 second boot up
    if(screenFlashDone){
        if(globalTime > screenFlashes[0]) { // play screen flash seq
            screenFlashes.shift();
            screenFlash(ctx,true,screenFlashes.shift(),screenFlashes.shift());
        }
    }else{
         screenFlash(ctx);
    }
    
    ctx.font = ((bootUp[1] * em) | 0) + "px monospace";
    ctx.textAlign = "center";
    ctx.textBaseline = "center";
    var tx = randG(-em4 * 4, em4 * 4, 64);  // G for kind of a bit like gausian. Last num controls distrubution
    var ty = randG(-em4 * 4, em4 * 4, 64);
    var xx = size / 2 + tx;
    var yy = em * 2 + ty;
    ctx.fillStyle = `hsl(${randG(160,250,32)|0},100%,50%)`;
    if(bootUp[2] < globalTime){
        bootUp.shift();
        bootUp.shift();
        bootUp.shift();
    }
    ctx.fillText(bootUp[0], xx, yy);
    ctx.save(); // need the normal non mirror transform for the noise FX
    ctx.setTransform(1,0,0,1,0,0);
    for(var y = -em/1.2|0; y < em/2; y += 1){
        if((yy+y) % 3 === 0){
            ctx.clearRect(0,yy+y,size,1);  // give scan line look
            
        }else{
            if(Math.random() < 0.1){ // only on 10% of lines.
                ctx.drawImage(ctx.canvas,0,yy + y, size, 2,randG(-em4 * 4,em4 * 4,32),yy + y, size, 2);
            }
        }
    }
    ctx.fillRect(0,((globalTime / 4000) * hSize)%hSize,size,2);
    
    ctx.filter = `blur(${randG(em4/2,em4,2)|0}px)`;
    ctx.drawImage(ctx.canvas,0,0);
    ctx.restore();
}
const screenFlashes = [0,500,3,1000,200,2,4000,100,3,6000,100,1,7500,50,1,7800,50,1, 9000];

var screenFlashStart;
var screenFlashLen;
var screenFlashDone = true;
var screenFlashLayers = 1;
function screenFlash(ctx,start,length,layers){
    if(start){
        screenFlashStart = globalTime;
        screenFlashLen = length;
        screenFlashDone = false;
        screenFlashLayers = layers;
    }
    var normTime = (globalTime - screenFlashStart) / screenFlashLen;
    if(normTime >= 1){
        screenFlashDone = true;
        normTime = 1;
    }
    for(var i = 0; i < screenFlashLayers; i++){
        var tx = randG(-em4 * 4, em4 * 4, 64);  // G for kind of a bit like gausian. Last num controls distrubution
        var ty = randG(-em4 * 4, em4 * 4, 64);
        ctx.globalAlpha = (1-normTime) * Math.random();
        ctx.fillStyle = `hsl(${randG(160,250,32)|0},100%,50%)`;
        ctx.fillRect(tx,ty,size,hSize);
    }
    ctx.globalAlpha = 1;
}

function randomBlur(ctx) {
    ctx.save(); // need the normal non mirror transform for the noise FX    
    ctx.filter = `blur(${randG(em4/2,em4,2)|0}px)`;
    ctx.drawImage(ctx.canvas,0,0);
    ctx.restore();
}

function ready(ctx) {
    ctx.fillStyle = "#0F0";
    ctx.font = em + "px monospace";
    ctx.textAlign = "center";
    ctx.textBaseline = "center";
    ctx.fillText("Holographic",hSize,em);
    ctx.font = em/2 + "px monospace";
    ctx.fillText("display ready.",hSize,em * 2);
    // draw edges
    ctx.strokeStyle = "#0F0";
    ctx.lineWidth = em4;
    ctx.beginPath();
    ctx.lineTo(0,0);
    ctx.lineTo(size,0);
    ctx.lineTo(hSize + deadZone, hSize - deadZone);
    ctx.lineTo(hSize - deadZone, hSize - deadZone);    
    ctx.closePath();
    ctx.stroke();
}
function renderContent(ctx){
    // all rendering is mirrored, but the transform takes care of that for you
    // just render as normal. Remember you can only see the
    // triangular area with the wide part at the top
    // and narrow at the bottom.
    
    // Anything below hSize - deadZone will also not appear
    if(globalTime < 8000){
        noisyText(ctx);
        randomBlur(ctx);
    }else{
        ready(ctx);
    }
    randomBlur(ctx);
    

}

一个简短的旁注。我觉得你的问题符合 SO 要求并且不是题外话,你也不是在找人写代码。你已经表明你在研究上付出了一些努力。这个问题会引起其他人的兴趣。我希望这个答案对您有所帮助,祝您项目顺利,欢迎来到 SO。