通过字母在画布上绘制文本,以控制单个字母的alpha,同时与中心对齐

问题描述 投票:0回答:2

我试图以非常具体的方式在画布上绘制一些字母 - 能够定位单个字母并应用alpha。这些单词需要以基线为中心,并在画布中对齐中心并填充strokeText而不是填充样式。

文本也需要断行,例如;

enter image description here

现在,我已经尝试了几种方法来解决这个问题 - 当写出单词(作为完整单词)时它工作正常(没有淡入淡出) - 但是当我尝试将它们写成单独的字母时,我无法正确地将它们置于中心位置。我的代码在下面省略了特定字母上的alpha,一旦我能正确地将事物放在中心,这应该不是问题!

我意识到问题是我试图在画布上分别以0为中心绘制每个字母,并为每个字母添加字母间距,但考虑到中间线的不同大小,我无法想办法让它们居中!

var can = document.querySelector('canvas'),
    ctx = can.getContext('2d');
    
 function drawStroked(text, fontSize, color, offsetX, offsetY) {
        let line = text.split('\n');
        this.ctx.font = fontSize + 'px ' + 'TimesNewRoman';
        this.ctx.strokeStyle = color;
        this.ctx.lineWidth = 2;
        this.ctx.textBaseline = 'middle';
        this.ctx.textAlign = 'center';
        let positionX = this.ctx.canvas.width/3;
        let positionY = this.ctx.canvas.height/4;

        if(offsetX !== 0) {
            positionX += offsetX;
        }

        if(offsetY !== 0) {
            positionY += offsetY;
        }
        for (var i = 0; i < line.length; i++) {
            for (var j = 0; j < line[i].length; j++) {
                let letterSpacing = 0;
                let lineHeight = positionY;

                if(line[i][j] === line[i].length) {
                    lineHeight = lineHeight * i;
                }
            this.ctx.strokeText(line[i][j], positionX + (letterSpacing + (j*130)), positionY + (i*fontSize));
            }
        }
    }
    
drawStroked('THIS\nIS THE\nTEXT', 100, '#000', 0, 0);
<canvas width="1000" height="1000"></canvas>

由Blindman67产生的输出和完成的代码!

enter image description here

const Hero = class {
    constructor(pos, canvas) {
        this.position = document.getElementById(pos);
        this.canvas = document.getElementById(canvas);
        this.height = document.getElementsByClassName('home')[0].clientHeight;
        this.width = this.position.offsetWidth;
        this.ctx = this.canvas.getContext('2d');
        this.title = 'THIS\nIS THE\nTEXT';
        this.canvas.width = this.width;
        this.canvas.height = this.height;

        this._init_ui();
    }

    // Draw text to text canvas
    _init_ui() {
        // BLUE
        this.drawStroked(300, '#1816ff', -3, 2, 0.95, [1, 5, 9, 12]);
        // GREEN
        this.drawStroked(300, '#1bff32', 0, 0, 0.95, [1, 5, 9, 12]);
        // RED
        this.drawStroked(300, '#ff162f', 3, -2, 0.95, [1, 5, 9, 12]);
    }

    drawStroked(fontSize, color, offsetX, offsetY, textVertSpacing, fade) {
        // Random Char's to scramble through --- to do
        // let chars = '!<>-_\\/[]{}—=+*^?#________';
        // The words
        let line = this.title.split('\n');
        // Set the font + size
        this.ctx.font = fontSize + 'px ' + 'Kommissar';
        // Set the colour - NEED TO ADD ALPHA LOGIC
        this.ctx.strokeStyle = color;
        // Set the stroke width
        this.ctx.lineWidth = 1;
        // Set the baseline
        this.ctx.textBaseline = 'middle';
        // Set the align
        this.ctx.textAlign = 'center';

        let positionX = this.width/2;
        let positionY = this.height/4;
        positionX += offsetX;
        positionY += offsetY;

        let charIndex = 0;

        for (var i = 0; i < line.length; i++) {
            // get the width of the whole line
            let width = this.ctx.measureText(line[i]).width;
            console.log(width);
            // use the width to find start
            var textPosX = positionX - width / 2;

            for (let j = 0; j < line[i].length; j++) {
                // get char
                let char = line[i][j];
                // get its width
                let cWidth = this.ctx.measureText(char).width;
                // check if char needs to fade
                if (fade.indexOf(charIndex) > -1) {
                    this.ctx.globalAlpha = 0.2;
                } else {
                    this.ctx.globalAlpha = 1;
                }
                // draw the char offset by half its width (center)
                this.ctx.strokeText(char, textPosX + cWidth / 2, positionY);
                // move too the next pos
                textPosX += cWidth;
                // count the char
                charIndex += 1;
            }
            // move down one line
            positionY += fontSize * textVertSpacing;
        }
    }
};

export default Hero;
javascript canvas text html5-canvas
2个回答
1
投票

Use ctx.measureText

你需要使用ctx.measureText并获得每个角色的宽度,然后你可以正确地隔开它们。

正确的字符间距

因为你有对齐中心,你必须将角色的宽度移动一半,然后绘制它,然后再移动一半的宽度。字符中心之间的间距是每个添加的宽度的一半。因此,如果"I"20像素宽,"W"是60那么他们之间的空间是10 + 30 = 40;

淡化字符

为了做淡入淡出,我传递了一个数组,其中字符的索引为淡入淡出。我每次画一个角色都算数。要检查字符是否应该淡入,请检查索引数组中的字符数。如果它们匹配则淡化该角色。

有关更多信息,请参阅示例

简单的例子......

...我认为你想要的。我添加了两条红线以确保对齐正确。

const ctx = canvas.getContext('2d');
ctx.fillStyle = "#FDD"; // mark the center
ctx.fillRect(canvas.width / 2 | 0, 0, 1, canvas.height);
ctx.fillRect(0, canvas.height / 2 | 0, canvas.width, 1);
ctx.fillStyle = "black";

// textVertSpacing is fraction of FontSize
// fade is the index of characters to fade, including spaces
// centerX and y is center of all text
function drawStroked(text, fontSize, color, centerX, centerY, textVertSpacing, fade) {
  let line = text.split('\n');
  ctx.font = fontSize + 'px ' + 'TimesNewRoman';
  ctx.strokeStyle = color;
  ctx.lineWidth = 2;
  ctx.textBaseline = 'middle';
  ctx.textAlign = 'center';
  // to count each character 
  var charIndex = 0;
  // find the top ypos and then move down half a char space
  var yPos = centerY - fontSize * line.length * 0.5 * textVertSpacing + fontSize * textVertSpacing / 2;
  
  for (var i = 0; i < line.length; i++) {
    // get the width of the whole line
    var width = ctx.measureText(line[i]).width;
    // use the width to find start
    var textPosX = centerX - width / 2;
    for (var j = 0; j < line[i].length; j++) {
      // get char
      var char = line[i][j];
      // get its width
      var cWidth = ctx.measureText(char).width;
      // check if char needs to fade
      if (fade.indexOf(charIndex) > -1) {
        ctx.globalAlpha = 0.5;
      } else {
        ctx.globalAlpha = 1;
      }
      // draw the char offset by half its width (center)
      ctx.fillText(char, textPosX + cWidth / 2, yPos);
      // move too the next pos
      textPosX += cWidth;
      // count the char
      charIndex += 1
    }
    // move down one line
    yPos += fontSize * textVertSpacing;
  }
}

drawStroked('THIS\nIS THE\nTEXT', 60, '#000', canvas.width / 2, canvas.height / 2, 0.9, [2, 4, 8, 12]);
<canvas id="canvas" width="500" height="200"></canvas>

Update

通过添加动画循环并每隔几帧调用文本渲染功能,为文本添加了一些闪烁。闪烁是通过随机化alpha来完成的。有关详细信息,请参阅下面的代码段。

requestAnimationFrame(animLoop);
const flickerRate = 4; // change alpha every 4 frames
var frameCount = 0;
const ctx = canvas.getContext('2d');
ctx.fillStyle = "#FDD"; // mark the center
ctx.fillRect(canvas.width / 2 | 0, 0, 1, canvas.height);
ctx.fillRect(0, canvas.height / 2 | 0, canvas.width, 1);
ctx.fillStyle = "black";


function drawStroked(text, fontSize, color, centerX, centerY, textVertSpacing, fade) {
  let line = text.split('\n');
  ctx.font = fontSize + 'px ' + 'TimesNewRoman';
  ctx.strokeStyle = color;
  ctx.lineWidth = 2;
  ctx.textBaseline = 'middle';
  ctx.textAlign = 'center';
  var charIndex = 0;
  var yPos = centerY - fontSize * line.length * 0.5 * textVertSpacing + fontSize * textVertSpacing / 2;
  
  for (var i = 0; i < line.length; i++) {
    var width = ctx.measureText(line[i]).width;
    var textPosX = centerX - width / 2;
    for (var j = 0; j < line[i].length; j++) {
      var char = line[i][j];
      var cWidth = ctx.measureText(char).width;
      ctx.globalAlpha = fade.indexOf(charIndex) > -1 ? Math.random()* 0.5+0.25 : 1;
      ctx.fillText(char, textPosX + cWidth / 2, yPos);
      textPosX += cWidth;
      charIndex += 1
    }
    yPos += fontSize * textVertSpacing;
  }
}
function animLoop(){
  if((frameCount % flickerRate) === 0){
    ctx.clearRect(0,0,canvas.width,canvas.height);

    drawStroked('THIS\nIS THE\nTEXT', 60, '#000', canvas.width / 2, canvas.height / 2, 0.9, [2, 4, 8, 12]);
  }
  frameCount ++;
  requestAnimationFrame(animLoop);
}
<canvas id="canvas" width="500" height="200"></canvas>

0
投票

在webpack设置中使用它很抱歉它不会运行!

// Code in UTIL
getRandomInt(max) {
  return Math.floor(Math.random() * (max - 0 + 1)) + 0;
};

const $window = $(window);

let running = false;

const Hero = {
    init() {
        this.home          = $('#home');
        this.position      = $('#hero');
        this.canvas        = $('#title');
        this.ctx           = this.canvas[0].getContext('2d');
        this.width         = this.position.width();
        this.height        = this.home.height();
        this.ctx.lineWidth = 1.5;
        this.fontSize      = null;
        this.letterSpacing = null;

        if(this.position.lenth === 0) {
            return;
        }

        if(running) {
            return;
        }

        // Set hero opacity to 0 for animation
        // $('#hero').css('opacity', 0);

        this.size();

        $window.on('resize', () => {
            clearTimeout(this.debounce);
            this.debounce = setTimeout( () => {
                this.height = this.home.height();
                this.width = this.position.width();

                this.size();
            }, 50);
        });
    },

    size() {
        running = true;

        this.canvas[0].width = this.width;
        this.canvas[0].height = this.height;

        if(this.width < 1000) {
            this.fontSize = 150;
            this.letterSpacing = 5;
        } else {
            this.fontSize = 300;
            this.letterSpacing = 30;
        }
    },

    animate(frames) {
        var frameCount = frames || 0;
        const flickerRate = 4;
        const fade = [Utils.getRandomInt(13), Utils.getRandomInt(13)];

       if((frameCount % flickerRate) === 0){
            this.ctx.clearRect(0, 0, this.width, this.height);
            // Blue
            this.drawStroked(this.fontSize, '#0426ff', -2, 2, true, fade);
            // Green
            this.drawStroked(this.fontSize, '#04ffae', 1, 2, true, fade);
            // Pink
            this.drawStroked(this.fontSize, '#ff29ad', 0, 0, true, fade);
            // White
            this.drawStroked(this.fontSize, '#fff', 0, 0, true, fade);
        }

        frameCount ++;
        console.log(frameCount);
        // requestAnimationFrame(this.animate);
        setTimeout(() => {
            this.animate(frameCount);
        }, 0.5);
    },

    drawStroked(fontSize, color, offsetX, offsetY, flicker, fade) {
        let line  = 'CODE\nIN THE\nDARK'.split('\n'),
            chars = line.join('');
        // Set the font + size
        this.ctx.font = fontSize + 'px ' + 'Kommissar';
        // Set the colour
        this.ctx.strokeStyle = color;
        // Set the baseline
        this.ctx.textBaseline = 'middle';
        // Set the align
        this.ctx.textAlign = 'center';

        let letterSpacing = this.letterSpacing,
            positionX = (this.width/2 + letterSpacing) + offsetX,
            positionY = (this.height/4) + offsetY,
            charIndex = 0;

        for (var i = 0; i < line.length; i++) {
            // get the width of the whole line
            let width = this.ctx.measureText(line[i]).width;
            // use the width to find start
            var textPosX = positionX - width / 2;

            for (let j = 0; j < line[i].length; j++) {
                // get char
                let char = line[i][j];
                // get its width
                let cWidth = this.ctx.measureText(char).width;
                // check if char needs to fade
                if(flicker) {
                    this.ctx.globalAlpha = fade.indexOf(charIndex) > -1 ? Math.random() * 0.5 + 0.25 : 0;
                } else {
                    this.ctx.globalAlpha = 1;
                }
                // draw the char offset by half its width (center)
                this.ctx.shadowColor = color;
                this.ctx.shadowBlur = 15;
                this.ctx.strokeText(char, textPosX + cWidth / 2, positionY);
                // move too the next pos
                textPosX += cWidth;
                // count the char
                charIndex += 1;
            }
            // move down one line
            positionY += fontSize * 1.05;
        }
    }
};

export default Hero;
#home {
   width: 100%;

  #hero {
      position: absolute;
      z-index: 5;
      top: 0;
      left: 0;
      width: 100%;
      padding: 30px 0;

      > canvas {
          margin: 0 auto;
          display: block;
      }
  }
}
<div id="home">
    <div id="hero">
      <canvas id="title"></canvas>
    </div>
</div>
© www.soinside.com 2019 - 2024. All rights reserved.