将对象放在另外两个之间

Posted

技术标签:

【中文标题】将对象放在另外两个之间【英文标题】:Place object between another two 【发布时间】:2015-05-17 04:50:57 【问题描述】:

我是画布的新手,我想将我的 DOM (html+JS) 动画移动到画布上。我试图在Basic animations - Web API Interfaces | MDN 中模仿太阳系动画,并且成功地做到了。 Fiddle.

我对@9​​87654324@ 有疑问。在动画中,地球正在投射阴影(低不透明度矩形),当月球绕着这个黑暗区域运行时,月亮应该在阴影矩形下方。如果我使用

context.globalCompositeOperation = 'destination-over';

draw 函数的顶部,我得到了预期的结果(fiddle),但随后月球和地球在轨道线下方旋转。我不想要那个。我希望地球和月亮都在轨道线上方,而月亮在阴影矩形下方。有没有办法做到这一点?如果没有,我将不得不摆脱阴影。

我的代码中没有使用context.save()context.restore(),尽管它们在原始动画中使用。我不熟悉他们。可能和我的问题有关...

var sun = 
  radius	: 80,
  x		: 0,
  y		: 0,
;
var earth = 
  radius	: 20,
  x		: sun.radius + 80,
  y		: 0,
  angle	: 0,
;
var moon = 
  radius	: 8,
  x		: 50,
  y		: 0,
  angle	: 0,
;

function draw() 
  var canvas	= document.getElementById("canvas");
  var context	= canvas.getContext("2d");

  context.resetTransform();
  context.clearRect(0, 0, canvas.width, canvas.height)
  context.translate(canvas.width/2, canvas.height/2);

  // sun
  context.beginPath();
  context.arc(0, 0, sun.radius, 0, 360 * Math.PI/180, false);
  context.fillStyle = "yellow";
  context.fill();

  // sun orbit
  context.beginPath();
  context.arc(0, 0, earth.x, 0, Math.PI*2, false);
  context.strokeStyle = "rgba(180,150,0,.25)";
  context.stroke();

  // earth
  earth.angle += .2;
  if (earth.angle == 360) 
    earth.angle = 0;
  
  context.globalCompositeOperation = 'source-over'; // earth is above the sun and its orbit
  context.rotate(earth.angle * Math.PI/180);
  context.translate(earth.x, 0);
  context.beginPath();
  context.arc(0, 0, earth.radius, 0, 360*(Math.PI/180), false);
  context.fillStyle = "royalblue";
  context.fill();
  context.globalCompositeOperation = 'destination-over'; // earth's orbit and shadow are below the earth

  // earth shadow
  context.fillStyle = "rgba(0,0,0,0.1)";
  context.fillRect(0, -earth.radius, earth.radius*16, earth.radius*2);

  // earth orbit
  context.beginPath();
  context.arc(0, 0, moon.x, 0, Math.PI*2, false);
  context.strokeStyle = "rgba(0,0,200,.2)";
  context.stroke();

  // moon
  moon.angle += 1;
  if (moon.angle == 360) 
    moon.angle = 0;
  
  context.globalCompositeOperation = 'source-over'; // moon is above the earth's orbit and shadow
  context.rotate(moon.angle * Math.PI/180);
  context.translate(moon.x, 0);
  context.beginPath();
  context.arc(0, 0, moon.radius, 0, 360 * Math.PI/180, false);
  context.fillStyle = "silver";
  context.fill();

  requestAnimationFrame(draw);


draw();
html 
  background: rgb(230,230,230);

canvas 
  background: white;
  border: 1px solid rgba(0,0,0,.2);
  box-shadow: 0 0 5px rgba(0,0,0,.1);
<canvas id="canvas" width="640" height="480"></canvas>

【问题讨论】:

【参考方案1】:

你只需要稍微改变一下代码中的顺序——在这种情况下真的不需要复合模式:

所有静态元素(如太阳和地球轨道)都可以渲染一次并设置为元素的背景。 先画月球轨道 第二次画月亮 第三个阴影 终于画出了地球。

在此更新中,我没有重新计算旋转等,而是使用了保存/恢复。

var sun = 
    radius	: 80,
    x		: 0,
    y		: 0,
;
var earth = 
    radius	: 20,
    x		: sun.radius + 80,
    y		: 0,
    angle	: 0,
;
var moon = 
    radius	: 8,
    x		: 50,
    y		: 0,
    angle	: 0,
;

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

context.setTransform(1,0,0,1,canvas.width/2, canvas.height/2);

// sun
context.beginPath();
context.arc(0, 0, sun.radius, 0, 360 * Math.PI/180, false);
context.fillStyle = "yellow";
context.fill();

// sun orbit
context.beginPath();
context.arc(0, 0, earth.x, 0, Math.PI*2, false);
context.strokeStyle = "rgba(180,150,0,.25)";
context.stroke();

canvas.style.backgroundImage = "url(" + canvas.toDataURL() + ")";

function draw() 

    context.setTransform(1,0,0,1,0,0);    
    context.clearRect(0, 0, canvas.width, canvas.height)
    context.setTransform(1,0,0,1,canvas.width/2, canvas.height/2);
    
    // earth
    earth.angle += .2;
    if (earth.angle == 360) 
        earth.angle = 0;
    
    context.rotate(earth.angle * Math.PI/180);
    context.translate(earth.x, 0);

        // earth orbit
    context.beginPath();
    context.arc(0, 0, moon.x, 0, Math.PI*2, false);
    context.strokeStyle = "rgba(0,0,200,.2)";
    context.stroke();
    
    // moon
    context.save();
    moon.angle += 1;
    if (moon.angle == 360) 
        moon.angle = 0;
    
    context.rotate(moon.angle * Math.PI/180);
    context.translate(moon.x, 0);
    context.beginPath();
    context.arc(0, 0, moon.radius, 0, 360 * Math.PI/180, false);
    context.fillStyle = "silver";
    context.fill();
    context.restore();

    // earth shadow
    context.fillStyle = "rgba(0,0,0,0.1)";
    context.fillRect(0, -earth.radius, earth.radius*16, earth.radius*2);

    context.beginPath();
    context.arc(0, 0, earth.radius, 0, 360*(Math.PI/180), false);
    context.fillStyle = "royalblue";
    context.fill();
    
    
    requestAnimationFrame(draw);


draw();
html 
    background: rgb(230,230,230);

canvas 
    background: white;
    border: 1px solid rgba(0,0,0,.2);
    box-shadow: 0 0 5px rgba(0,0,0,.1);
    <canvas id="canvas" width="640" height="480"></canvas>

【讨论】:

以上是关于将对象放在另外两个之间的主要内容,如果未能解决你的问题,请参考以下文章

将视图放在顶部和两个视图之间

将div放在两个div之间的边界顶部[重复]

在其他两个之间垂直居中视图

通过一个页面向另外的页面传参数的时候有两个方法

将两个相关对象的业务逻辑放在哪里?

合并一个值在另外两个之间的熊猫数据框[重复]