如何高效地在画布上加载和绘制表情符号?

8

我希望在HTML画布上打印一个表情符号。

可以使用图像实现,但这样很麻烦。有更好的方法吗?

const canvas = document.querySelector("#canvas");
const contex = canvas.getContext("2d");

var img = new Image();
img.src = "emoji.jpg";
img.addEventListener(
  "load",
  () => {
    context.drawImage(img, 0, 0, 200, 200);
  }
);
img.src = "img.jpg";
#canvas {
  background: red;
}
<canvas id="canvas"></canvas>

编辑:

好的,我认为我的问题被误解了。我能够在画布上绘制表情符号的图像。我不想这样做。因为在将它们打印到画布上之前,我必须截屏所有的表情符号并裁剪它们,这很麻烦。我正在寻找更有效的方法。


你的代码能否正常工作?你可以使用一些自定义字体,将字形放置为表情符号,例如:https://afeld.github.io/emoji-css/,但它仍然使用图像。 - Justinas
你可以看一下这支笔:Emo Bounce Boogie - enxaneta
@Justinas 我的代码可以运行。我正在寻找更好的方法。 - user9408899
我添加了一个新方案。 - Teocci
3个回答

13
您可以使用fillText方法来绘制Unicode表情符号。
您可以从emojipedia复制和粘贴表情符号。
虽然我不是专家,因此我不知道这种方式是否存在重大缺点,但无论如何,以下是一些需要考虑的事项:
  1. 这可能在某些浏览器/操作系统上无法正常工作。
  2. 标准表情符号在不同的浏览器/系统中可能看起来不同,除非您使用自定义字体。
  3. 您应该确保JS以UTF-8格式被读取才能使此方法正常工作。对于HTML5来说,这是标准的,所以您可能不需要做任何更改,除非出现问题。

const canvas = document.querySelector("#canvas");
const contex = canvas.getContext("2d");

// The size of the emoji is set with the font
contex.font = '100px serif'
// use these alignment properties for "better" positioning
contex.textAlign = "center"; 
contex.textBaseline = "middle"; 
// draw the emoji
contex.fillText('', canvas.width / 2, canvas.height / 2)
#canvas {
  background: #ccc;
}
<canvas id="canvas"></canvas>


2
我认为你需要另一种方法。首先,当我们使用CanvasRenderingContext2D.drawImage()方法时,我们需要: i) 加载图像,然后修改图像并将其绘制到画布上,因此,我们还需要 ii) 调整画布的大小。如果我们只使用Unicode表情符号字符提供的表情符号呢?
这个例子将帮助我们通过它的codepoint值使用一个表情符号。我们只需要通过String.fromCodePoint方法传递一个hexcode。该方法返回使用指定代码点序列创建的字符串。然后我们可以将该字符串作为表情符号打印到画布上。这种方法将节省大量时间来裁剪、调整大小和渲染每个表情符号。看看这个简短的例子:

const emojis = [
  0x1F600, 0x1F601, 0x1F603, 0x1F603, 0x1F604, 0x1F605, 0x1F606,
  0x1F607, 0x1F609, 0x1F60A, 0x1F642, 0x1F643, 0x1F355, 0x1F354,
]

const emoji = []
const size = 80
const factor = 2
const placeholder = document.getElementById('emojis')

for (let i = 0; i < 3; i++) {
  emoji.push({
    x: size,
    y: size,
    src: getEmoji()
  });
}

function loadCanvas(id, emo) {
  const canvas = document.createElement('canvas')
  canvas.id = id
  placeholder.appendChild(canvas)
  const ctx = canvas.getContext('2d')
  loadEmoji(canvas, ctx, emo)
}


function loadEmoji(canvas, ctx, emo) {
  // Use the intrinsic size of image in CSS pixels for the canvas element
  canvas.width = w = size * factor
  canvas.height = h = size * factor

  const fontSize = size * (factor - .5)
  const offset = Math.floor((h - fontSize) / 4)

  ctx.font = `${size * (factor - .5)}px Arial`
  ctx.textBaseline = 'middle'
  ctx.textAlign = 'center'

  emo.x = w / 2
  emo.y = h - size + offset

  ctx.fillText(emo.src, emo.x, emo.y)
}

function getEmoji() {
  const len = emojis.length
  const emos = Math.floor(Math.random() * len)

  return String.fromCodePoint(emojis[emos])
}


window.onload = function() {
  emoji.forEach((emo, i) => {
    const id = `canvas-0${i}`
    loadCanvas(id, emo)
  })
}
#emojis {
  display: flex;
  justify-content: center;
  align-items: center;
}

#emojis * {
  background: red;
}
<div id="emojis"></div>

注意

请注意,这可能无法在所有浏览器上工作,并且表情符号将根据其所显示的操作系统而变化。


不错!但是为什么要用“factor”? - backspaces
factor有助于在行内对齐情感,我们试图模拟的是一种排版,其中情感是x高度,红色区域是字体高度。因此,该因素有助于将表情符号居中放置在红色区域内。 - Teocci

0

如果您不想使用图片,那么我这里有一个纯编码的示例。正如您所看到的,它需要更多的编码。

index.html

<!doctype html>
<html lang="en">
<head>
<title>Smiley Face</title>
<meta charset="utf-8" />

   <link rel="stylesheet" href="css/smiley.css" >

</head>
<body>
    <canvas width="600" height="600" id="smiley">
        <p>You need canvas!</p>
        <p>This example requires a browser that supports the
        <a href="http://www.w3.org/html/wg/html5/">HTML5</a> 
        &lt;canvas&gt; feature.</p>
    </canvas>
    <script src="js/smiley.js"></script>
</body>
</html>

smiley.js

//smileyView Object literal
class smileyComponent {
  constructor() {
    //initialise window, cv & ctx, drawSmiley & call drawsmiley
    this._window = this;
    this._cv = document.getElementById('smiley');
    this._ctx = this._cv.getContext('2d');
    this.drawSmiley();
  }
  //getters
  get window() {
    return this._window;
  }
  get cv() {
    return this._cv;
  }
  get ctx() {
    return this._ctx;
  }

  drawArc(x, y, radius, startAngle, endAngle, clockwise) {
    this._ctx.arc(x, y, radius, startAngle, endAngle, clockwise);
  }
  drawLine(xs, ys, xe, ye) {
    this._ctx.moveTo(xs, ys);
    this._ctx.lineTo(xe, ye);
  }
  drawSmiley() {
    //initialise lineWidth & fillStyle
    this._ctx.fillStyle = "yellow";
    this._ctx.strokeStyle = "black";
    this._ctx.lineWidth = 5;

    //head
    this._ctx.beginPath();
    this.drawArc(300,300,200,smileyComponent.degreesToRadians(0),smileyComponent.degreesToRadians(360),true);
    this._ctx.fill();

    //left eye
    this._ctx.beginPath();
    this.drawArc(200,200,50,smileyComponent.degreesToRadians(0),smileyComponent.degreesToRadians(360),true);
    this._ctx.fillStyle = "black";
    this._ctx.fill();
    this._ctx.beginPath();
    this.drawArc(220,220,25,smileyComponent.degreesToRadians(0),smileyComponent.degreesToRadians(360),true);
    this._ctx.fillStyle = "white";
    this._ctx.fill();


    //right eye
    this._ctx.beginPath();
    this.drawArc(400,200,50,smileyComponent.degreesToRadians(0),smileyComponent.degreesToRadians(360),true);
    this._ctx.fillStyle = "black";
    this._ctx.fill();
    this._ctx.beginPath();
    this.drawArc(420,220,25,smileyComponent.degreesToRadians(0),smileyComponent.degreesToRadians(360),true);
    this._ctx.fillStyle = "white";
    this._ctx.fill();

    //nose
    this._ctx.beginPath();
    this.drawLine(300,350,300,250);
    this._ctx.stroke();


    //smile
    this._ctx.beginPath();
    this.drawArc(300,300,150,smileyComponent.degreesToRadians(160),smileyComponent.degreesToRadians(380),true);
    this._ctx.stroke();
  }

  static degreesToRadians(degrees) {
    return degrees * Math.PI / 180;
  }
}
window.onload = () => new smileyComponent();

你可以在这里检查结果。


为什么这里有冗余的getter方法?你可以直接去掉属性名称中的下划线。 - Taureon

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接