画布的背景无意中走到了前面

时间:2019-07-15 22:10:25

标签: node.js canvas dialogflow actions-on-google

我有9个插槽,希望在我的动作中的对话过程中能充满元素。 frame()函数设计交互式画布的背景。因此,我想一开始称之为一次。问题是当我绘制第二个元素时,前一个元素消失了。我已经弄清了原因。frame()似乎每次都重新绘制背景。

我尝试在Default Welcome Intent的回调中调用frame(),但没有任何改变。我还使函数noop()foo()只能调用frame()一次..没有结果。 然后,我尝试在将背景置于最前面之后每次重绘以前的元素,但是那也不起作用。 我也无法使用div在index.html中重新创建插槽。矩形出现在整个画布框架的上方或下方。

有人知道如何解决这个问题吗?还是您认为最简单的解决方案是创建背景的.jpg并将其设置为html文件中画布的style="background url('...')"

感谢您的帮助。

'use strict;'
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');

// failed attempt to redraw previous elements after
// unintentionally bringing background to front:
var prevReqs=[]; //see line 26 and 78

var iD=1;
var requirements = [
  new Requirement(50,50),
  new Requirement(500,50),
  new Requirement(950,50),
  new Requirement(50,250),
  new Requirement(500,250),
  new Requirement(950,250),
  new Requirement(50,450),
  new Requirement(500,450),
  new Requirement(950,450),
];

const callbacks = {
  onUpdate(state) {
    if ('iD' in state) {
      iD = state.iD;
      // prevReqs.push(iD);
      drawRec();
    }
  },
};

function drawRec(){
  requestAnimationFrame(drawRec); //enable drawing
  requirements[iD-1].drawRequirement();
}

function Requirement(xCoord,yCoord){
  this.xCoord=xCoord;
  this.yCoord=yCoord;
  this.drawRequirement = function(){ //draw the element
    ctx.beginPath();
    ctx.lineWidth="1";
    ctx.strokeStyle="#000000";
    ctx.fillStyle="#FFFFFF";
    ctx.rect(xCoord,yCoord,200,100);
    ctx.fill();
    ctx.stroke();
    ctx.moveTo(xCoord,yCoord+33);
    ctx.lineTo(xCoord+200,yCoord+33);
    ctx.stroke();
    ctx.font = "12px Arial";
    ctx.fillStyle = "#000000";
    ctx.strokeStyle="#000000";
    ctx.textAlign = "left";
    ctx.fillText("id: "+iD, xCoord+5, yCoord+47);
  }
}

//background... the problematic function
function frame(){
  requestAnimationFrame(frame);
  ctx.fillStyle="#D6EAF8";
  ctx.strokeStyle="#D6EAF8";
  ctx.rect(0,0,1200,600);
  ctx.stroke();
  ctx.fill();
  for(let i=0;i<requirements.length;i++){
    ctx.beginPath();
    ctx.rect(requirements[i].xCoord,requirements[i].yCoord,200,100);
    ctx.font = "64px Arial";
    ctx.fillStyle = "#DCDCDC";
    ctx.strokeStyle="#DCDCDC";
    ctx.textAlign = "center";
    ctx.lineWidth = "6";
    ctx.fillText(i+1, requirements[i].xCoord+100, requirements[i].yCoord+72);
    ctx.stroke();
  }
  // for(let p=0;p<prevReqs.length;p++){
  //    if(prevReqs[p]!=null){
  //     requirements[prevReqs[p]].drawRequirement();
  //    }
  // }
}

// attempt to call frame() only once
function noop() {};
function foo() {
    foo = noop;
    frame();
}
foo();

// frame(); //?

drawRec(); //uncomment to test in html
assistantCanvas.ready(callbacks);
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <title>test</title>

    <style type="text/css">
        canvas {
            border: 6px solid navy;
            background: #D6EAF8;
        }
    </style>

    <!-- Disable favicon requests -->
    <link rel="shortcut icon" type="image/x-icon" href="data:image/x-icon;,">

    <!-- Load Assistant Canvas CSS and JavaScript -->
    <link rel="stylesheet" href="https://www.gstatic.com/assistant/immersivecanvas/css/styles.css">
    <script src="https://www.gstatic.com/assistant/immersivecanvas/js/immersive_canvas_api.js"></script>

  </head>
  <body>
    <canvas width="1200" height="600" padding-left="56px"></canvas>
    <script src="js/main.js"></script>
    <!--<script src="js/log.js"></script>-->
  </body>
</html>

'use strict';

const {
  dialogflow,
  Suggestions,
  ImmersiveResponse,
} = require('actions-on-google');
// Import the firebase-functions package for deployment.
const functions = require('firebase-functions');
// Instantiate the Dialogflow client.
const app = dialogflow({debug: true});
const firebaseConfig = JSON.parse(process.env.FIREBASE_CONFIG);

// Handle the Dialogflow intent named 'Default Welcome Intent'.
app.intent('Default Welcome Intent', (conv) => {
  conv.ask('Hello');
  conv.ask(new ImmersiveResponse({
    url: `https://${firebaseConfig.projectId}.firebaseapp.com`,
  }));
});

// Handle the Dialogflow intent named 'PlaceElement'
app.intent('PlaceElement', (conv, {number}) => {
  if(number>0 && number<10){
    conv.ask('Ok...');
    conv.ask(new ImmersiveResponse({
      state: {
        iD: number,
      },
    }));
  }
  else {
    conv.ask('Wrong input.');
  }
});

exports.yourAction = functions.https.onRequest(app);

1 个答案:

答案 0 :(得分:2)

我怀疑requestAnimationFrame()并没有按照您的想法做(根据您的使用方式和旁边的评论来判断)。它不会“启用绘图”。

其作用是要求在渲染引擎下次进行重新绘制之前调用如此命名的函数。这可以用来更新动画(因此得名),但也可以用于非动画用途。在动画中使用时,通常具有执行绘图调用requestAnimationFrame()的功能,再次将其命名为同一功能-将其置于无限循环中。

这就是您在这里所做的。您认为,您只打过一次frame(),但是在那之后,您就可以拨打电话

requestAnimationFrame(frame);

它将呈现函数中的内容,然后在重新渲染布局(通常愿意每秒执行60次左右)之前,它会重新调用函数。一遍又一遍。

frame()继续被调用的原因是因为它要求继续被调用。

根据您的实际需求,您可以采用两种方法:

  1. 只需删除该行。除非您正在制作动画,否则没有必要。
  2. 如果您正在根据iD的值来制作动画,那么您的onUpdate可以只设置iD的值并拥有{{1} }根据iD绘制事物。

顺便说一句,在您的情况下情况并非如此,但是发布Webhook代码的一个原因是准确查看您在frame()对象中发送的内容。如果您每次都发送ImmersiveResponse参数,那么您每次都重新加载页面(从而重新绘制背景)。