在画布中创建可滚动文本

时间:2017-06-11 21:34:24

标签: javascript canvas html5-canvas

我要在画布中插入一个大文本。

首先我将文本换行,现在我试图让它在画布中滚动。

    context.wrapText = function(text, x, y, maxWidth, lineHeight) {
    var y_initial = y;
    var cars = text.split("\n");
    for (var ii = 0; ii < cars.length; ii++) {
        var line = "";
        var words = cars[ii].split(" ");
        for (var n = 0; n < words.length; n++) {
            var testLine = line + words[n] + " ";
            var metrics = context.measureText(testLine);
            var testWidth = metrics.width;
            if (testWidth > maxWidth) {
                context.fillText(line, x, y);
                line = words[n] + " ";
                y += lineHeight;
            }
            else {
                line = testLine;
            }
        }
        context.fillText(line, x, y);
        y += lineHeight;
    }
    return y - y_initial;
}

context.wrapText("hello dear friends\nhere is\na sample of text\nthis is juste an example\nthanks\nbut need to be scrollable\n it works?\nlet's try.", 120, 80, 200, 15);

谢谢你的帮助。

1 个答案:

答案 0 :(得分:1)

画布只是一个像素商店。它不记得除了像素之外绘制的内容。要进行滚动框,每次移动时都需要渲染整个滚动框。

上下文允许您设置剪辑区域。您可以使用它来确保不显示超出边界的文本。

使用ctx.setTransform设置滚动位置进行渲染。如果向上滚动,滚动位置必须进入否定状态。

演示是文本滚动框的一个非常基本的示例。它没有优化,文本拟合没有真正的智慧。

请参阅代码了解详细信息,它是非常基本的代码,所以没有那么多评论。如果您有疑问,请在评论中提出。

没有鼠标或用户互动,我留给你。

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

const textScrollBox = {
     dirty : true, // indicates that variouse setting need update
     cleanit(dontFitText){
         if(this.dirty){
             this.setFont();
             this.getTextPos();
             this.dirty = false;
             if(!dontFitText){
                this.fitText();
             }
         }         
     },
     scrollY : 0,
     fontSize : 24,
     font : "Arial",
     align : "left",
     background : "#999",
     border : {
         lineWidth : 4,
         style : "black",
         corner : "round",
     },
     scrollBox : {
         width : 10,
         background : "#568",
         color : "#78a",
     },
     fontStyle : "black",
     setOptions(options){
         Object.keys(this).forEach((key) =>{
             if(options[key] !== undefined){
                 this[key] = options[key];
                 this.dirty = true;
             }
         })
     },
     setFont(){
        this.fontStr = this.fontSize + "px " + this.font;
        this.textHeight = this.fontSize + Math.ceil(this.fontSize * 0.05);
     },
     getTextPos(){
        if(this.align === "left"){ this.textPos = 0 }
        else if(this.align === "right"){ this.textPos = Math.floor(this.width - this.scrollBox.width -this.fontSize / 4) }
        else { this.textPos = Math.floor((this.width- - this.scrollBox.width) / 2) }
     },  
     fitText(){
         this.cleanit(true); // MUST PASS TRUE or will recurse to call stack overflow

         ctx.font = this.fontStr;
         ctx.textAlign = this.align;
         ctx.textBaseline = "top";
         var words = this.text.split(" ");
         this.lines.length = 0;
         var line = "";
         var space = "";
         while(words.length > 0){
             var word = words.shift();
             var width = ctx.measureText(line + space +  word).width;
             if(width < this.width - this.scrollBox.width - this.scrollBox.width){
                 line += space + word;
                 space = " ";
             }else{
                 if(space === ""){ // if one word too big put it in anyways
                     line += word;
                 }else{
                     words.unshift(word);
                 }
                 this.lines.push(line);
                 space = "";
                 line = "";
             }
         }
         if(line !== ""){
             this.lines.push(line);
         }
         this.maxScroll = ((this.lines.length + 0.5) * this.textHeight) - this.height;
     },             
     drawBorder(){
         var bw = this.border.lineWidth / 2;
         ctx.lineJoin = this.border.corner;
         ctx.lineWidth = this.border.lineWidth;
         ctx.strokeStyle = this.border.style;
         ctx.strokeRect(this.x - bw,this.y - bw,this.width + 2 * bw,this.height + 2 * bw);
     },
     drawScrollBox(){
         var displayHeight = this.height;
         var scale = this.height / (this.lines.length * this.textHeight);
         ctx.fillStyle = this.scrollBox.background;
         ctx.fillRect(
            this.x + this.width - this.scrollBox.width,
            this.y,this.scrollBox.width,this.height
         )
         ctx.fillStyle = this.scrollBox.color;
         ctx.fillRect(
            this.x + this.width - this.scrollBox.width,
            this.y - (this.scrollY * scale),
            this.scrollBox.width,this.height * scale
         )
     },
     scroll(pos){
        this.cleanit();        
        this.scrollY = -pos;
        if(this.scrollY > 0){
            this.scrollY = 0;
        }else if(this.scrollY < -this.maxScroll ){
            this.scrollY = -this.maxScroll ;
        }
     },
     render(){
        this.cleanit(); 
         ctx.font = this.fontStr;
         ctx.textAlign = this.align;
         this.drawBorder();
         ctx.save(); // need this to reset the clip area

         ctx.fillStyle = this.background;
         ctx.fillRect(this.x,this.y,this.width,this.height);
         this.drawScrollBox();
         ctx.beginPath();
         ctx.rect(this.x,this.y,this.width-this.scrollBox.width,this.height);
         ctx.clip();         
         // Important text does not like being place at fractions of a pixel
         // make sure you round the y pos;
         ctx.setTransform(1,0,0,1,this.x, Math.floor(this.y + this.scrollY)); 
         ctx.fillStyle = this.fontStyle;

         for(var i = 0; i < this.lines.length; i ++){
             // Important text does not like being place at fractions of a pixel
             // make sure you round the y pos;
             ctx.fillText(this.lines[i],this.textPos,Math.floor(i * this.textHeight));
         }
         ctx.restore(); // remove the clipping
     }
}     
function createScrollText( text, x, y, width, height, options = {} ){
  return Object.assign({},
    textScrollBox,{
        text,x, y, width, height,
        lines : [],
    },
    options
  );

}
const text = "This is some random text added to the canvas via 2d API as a simple scroll box example at Stackoverflow. If you like any of the answers you find at stackoverflow and any of the other stack sites don't forget to upvote as a way to show your appreciation to all those that put in their free time to help others. Happy rendering "
var scrollText = createScrollText(text, 10, 10, 200, 180);
var scrollText1 = createScrollText(text, 220, 10, 270, 180);
scrollText1.setOptions({ // set only the properties you want to change
   fontStyle : "white",
   scrollBox : {  // if you add scrollbox you must put all property in for it
       width : 5,
       background : "#DDD",
       color : "#555",
   },
   border : { // if you add border you must put all property in for it
         lineWidth : 4,
         style : "black",
         corner : "round",
   },
   font : "Comic Sans MS",
   fontSize : 36,
   align :"center",
})  

function mainLoop(time){
    ctx.setTransform(1,0,0,1,0,0);
    ctx.clearRect(0,0,canvas.width,canvas.height);
    scrollText.scroll((Math.sin(time/3000) + 1) * scrollText.maxScroll * 0.5);
    scrollText1.scroll((Math.sin(time/3462) + 1) * scrollText1.maxScroll * 0.5);
    scrollText.render();
    scrollText1.render();
    requestAnimationFrame(mainLoop);
}
requestAnimationFrame(mainLoop);
&#13;
canvas {
border : 2px solid black;
}
&#13;
<canvas id="canvas" width=500 height=200></canvas>
&#13;
&#13;
&#13;