请求Canvas Text和requestAnimationFrame帮助

时间:2017-09-27 18:08:41

标签: javascript html canvas requestanimationframe

我已经在一个问题上工作了几天而且一直无法解决它。请注意,我对Javascript相对较新,所以不确定我下面的内容是否是实现这一目标的最佳方式,但过去几天经历这一点肯定有助于我学习一些新东西。

另外,请注意我知道我可以使用CSS轻松实现这一点,但我想知道是否有Javascript / JQuery解决方案。

问题:     我试图在画布上模拟一些文本的fadeIn动画。

var introText =
{
    "Welcome To A New Day...": "75",
    "Full Service Web Design": "50",
    "by": "50",
    "J. David Hock": "50"
};

数字代表字体大小。

以下是完整代码:

$(document).ready(function ()
{
    var canvas = $('#myCanvas')[0];
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    var ctx = canvas.getContext('2d');

    var introText =
    {
        "Welcome To A New Day...": "75",
        "Full Service Web Design": "50",
        "by": "50",
        "J. David Hock": "50"
    };


    function fadeText(timeStamp, t, x, y)
    {
        var opacity = timeStamp / 1000;
        console.log('Timestamp: ' + timeStamp + ' Opacity: ' + opacity);
        console.log('t, x, y |' + t +' | ' + x + ' | '  + y)
        //ctx.clearRect(0, 0, canvas.width, canvas.height);
        //ctx.fillStyle = 'rgba(178, 34, 34, ' + opacity + ')';
        //ctx.fillText(t, x, y);

        if (opacity < 1)                   
        {
            requestAnimationFrame(function (timestamp)
            {
                fadeText(timestamp, t, x, y)
            });
        }
    }

    function MessageObject(x, y, f, fs, t)
    {
        this.x = x;
        this.y = y;
        this.f = f;
        this.fs = fs;
        this.t = t;
    }

    var msgArray = [];

    function CreateMessageArray(myArray, callback)
    {   
        var i = 0;
        var v = 75;

        var x = 0;
        var y = 0;
        var f = '';
        var fs = '';
        var t = '';


        for (t in myArray)
        {
            fs = myArray[t];                                                    //font size
            f = 'italic ' + fs + 'px Bradley Hand';                             //font type
            x = (canvas.width / 2)                                              //x pos of text
            msgArray.push(new MessageObject(x, y, f, fs, t))
            y = Number(fs);
            //alert('x, y, f, t | ' + x + ' | ' + y + ' | ' + f + ' | ' + t);
        }

        return callback(msgArray);
    }

    let ProcessMessageArray = function (myArray)
    {
        var xPrime = 0;
        var yPrime = 0;

        for (i = 0; i < myArray.length; i++)
        {
            var msgObject = myArray[i];

            var x = msgObject.x;
            var y = msgObject.y;
            var f = msgObject.f;
            var fs = msgObject.fs;
            var t = msgObject.t;

            ctx.textBaseline = 'top';
            ctx.font = f;
            var txtWidth = ctx.measureText(t).width

            xPrime = x - (txtWidth / 2);

            requestAnimationFrame(function(timestamp)
            {
                fadeText(timestamp, t, x, y)
            });

            //ctx.fillStyle = 'rgba(178, 34, 34, 1)';
            //ctx.fillText(t, xPrime, yPrime);

            if (i === 0)
            {
                yPrime = Number(yPrime) + (2 * Number(fs) - 35);
            }
            else
            {
                yPrime = Number(yPrime) + Number(fs);
            }
        }
    }

    CreateMessageArray(introText, ProcessMessageArray)

});

它应该起作用的方式是CreateMessageArray函数为introTextArray中的每一行文本创建一个包含x-pos,y-pos等的对象数组。

然后,ProcessMessageArray负责将introTextArray中的每一行文本输出到屏幕上的正确位置。

在ProcessMessageArray中有一个对requestAnimationFrame函数的调用,我希望它会“淡入”文本的每一行,但实际发生的是我只获取IntroTextArray中的最后一行文本。 / p>

我确信这与我在循环中调用requestAnimationFrame这一事实有关,但我不知道如何实现我想要的其他方式。任何意见,将不胜感激。

1 个答案:

答案 0 :(得分:0)

使用requestAnimationFrame进行动画

  • RAF requestAnimationFrame

您可以通过多种方式使用RAF进行动画处理。如果在下次刷新之前全部调用了内容,则可以使用许多RAF函数,这些函数将同时向显示器显示内容。但这会产生额外的开销(特别是如果每​​个项目的渲染负载很小)和额外的头部疼痛。如果你有一个缓慢的渲染(无论出于何种原因,浏览器可以在没有警告的情况下挂起1/60秒),它可能会破坏演示顺序,你只会获得渲染帧的一部分。

管理任何类型动画的最简单方法是使用通过RAF调用的单个动画循环。从该功能中,您可以调用动画。这对于演示顺序问题是安全的,并且可以轻松切换状态。 (例如,在文本淡出之后,您可能希望其他内容消失)

修复

你的代码无法保存,对不起,是DOA,所以我重写了整个事情。我在评论中添加了一些注释,说明为什么以及为什么,但如果您有疑问,请在评论中提问。

该示例将淡化文本显示为两组(用于说明更改显示状态,状态为摘要以及对相关元素或渲染的引用(例如,简介,中间,结尾))

该函数有一个主循环,用于处理渲染调用和时间。

文本由更新然后显示的功能显示。当文本已经淡入时它将返回true,因此主循环可以设置下一个渲染状态。

// Removed jQuery and I dont see the need to use it
// The convention in JavaScript is NOT to capitalize the first character
// unless you function / object is created using new ObjectName

const ctx = canvas.getContext("2d");  // canvas is named in HTML via its id
canvas.width = innerWidth; // window is the global scope you do not need 
canvas.height = innerHeight; //to prefix when accesing its properties

// helper
//const log = (...data) => console.log(data.join(","));

// use an array it is better suited to the data you are storing
const introText = [
  ["Testing one two.", 75],
  ["Testing..." , 50],
  ["One, one two.", 50],
  ["Is this thing on?", 50],
  ["",1], // to delay next state
  ["",1], // to delay next state
];
const introText1 = [
  ["Second stage, state 2", 20],
  ["The End" , 40],
  [":)", 30],
  ["",10],
  ["Thanks for watching..",12],
  ["",1],
  ["",1],
  ["Dont forget to vote for any answers you find helpfull..",10],
  ["",1],
  ["",10],
  ["This intro was brought to you by",12],
  ["",10],
  ["requestAnimationFrame",12],
  ["HTML5",12],
  ["canvas",12],
  ["JavaScript (AKA) ECMAScript6",12],
  ["",10],
  ["Staring...",14],
  ["Stackoverflow.com",18],
];
const TEXT_FADE_TIME = 1000; // in ms

// create the array of display arrays
const displayLists = [
   createDisplayList(8, introText), // converts simple text array to display list
   createDisplayList(8, introText1),
];
var curretDisplayListIdx = 0;

requestAnimationFrame(mainLoop); // will start when all code has been parsed

var startTime;
function mainLoop(time){  // keep it simple use one animation frame per frame
  if(startTime === undefined) { startTime = time }
  const timeSinceStart = time - startTime;
  ctx.clearRect(0,0, canvas.width, canvas.height); 
  if (textFadeIn(timeSinceStart, curretDisplayListIdx )) {
     if (curretDisplayListIdx < displayLists.length - 1) {
         curretDisplayListIdx += 1;
         startTime = time;
     }
  }
  
  requestAnimationFrame(mainLoop);
}

// creates a display list from text array. top is the start y pos
function createDisplayList(top, array) {
  const result = [];
  var y = top;
  var fontSize;
  for (const item of array) {  
    const fontSize = item[1];
    result.push({
      font : 'italic ' + fontSize + 'px Bradley Hand',
      text : item[0],    
      x : canvas.width / 2,
      y , fontSize,
      startTime : null,
      fadeTime : TEXT_FADE_TIME,
      alpha : 0, // starting alpha
    });
     y += fontSize;
  }
  return result;
}
// displays a text display list from the displayLists array. 
// time is time since starting to display the list 
// displayListIdx is the index
// returns true when text has faded in
function textFadeIn(time, displayListIdx) {
  // complete will be true when all have faded in
  const complete = updateDisplayList(displayLists[displayListIdx], time);
  renderDisplayList(displayLists[displayListIdx]);

  return complete;

}

// separate logic from rendering
function updateDisplayList(array, time) {
  var fadeNext = true; // used to indicate that the next item should fade in
  // for each item
  for (const text of array) {
    if (fadeNext) { // has the previous items done its thing?
      if (text.startTime === null) { text.startTime = time }
    }
    if(text.startTime !== null){  // if start time is set then fade it in
        text.alpha = Math.min(1, (time - text.startTime) / text.fadeTime);
        if (text.alpha < 1) { fadeNext = false }  // if not complete flag fadeNext to stop next text fading in
    }
  }
  // if last item is fully faded in return true
  return array[array.length - 1].alpha === 1;

}

// seperate rendering from logic
function renderDisplayList(array) {
  ctx.textBaseline = "top";
  ctx.textAlign = "center";
  ctx.fillStyle = "black";
  for (const text of array) {
    ctx.font = text.font;
    ctx.globalAlpha = text.alpha;
    ctx.fillText(text.text,text.x,text.y);
  }
}
canvas {
  position: absolute;
  top: 0px;
  left: 0px;
}
<canvas id="canvas"></canvas>