canvas 的妙用

我们在做国际化网站的时候,必然会遇到多语言的场景,而同一个词语或者语句,翻译成不同国家的语言后,其长度是不一样的。

假设有这么一个场景:有一个固定宽度的按钮,按钮文案为中文的时候,它可以一行展示,但是翻译成英文或者其它语言的时候,长度超过了按钮的宽度,默认情况下是会换行展示成两行。现在需求是当文案超过按钮宽度之后,缩小文案,让文案始终展示成一行

分析上述需求,我们能获得实现需求的两个关键点:

  • 如何判断文案是否超过按钮的宽度?
  • 如何缩小文案,让文案展示成一行?

判断文案是否超过按钮的宽度

对于这个要求,我们脑海里第一个浮现的想法可能是:通过文案的长度来判断其是否超出了按钮,但是这样的做法好像不太行,因为文案的长度和文案的宽度不挂钩,对于同一长度的文案,其在页面上渲染的宽度是不一样的,比如 iii 和 GGG,它们的长度虽然都是 3 ,但是显然 GGG 的宽度比 iii 的大。

想要正确地测量出文案的宽度,这时候我们就需要用到 canvas 画布了,我们知道 canvas 除了可以被用来绘制图形,它也可以用来绘制文本,更重要的是,它提供了测量文本的方法:measureText()

我们来测量下 iii 和 GGG 的宽度,代码如下:

<body>
	 <canvas id="canvas" width="200" height="100" style="border:1px solid #000000;"></canvas>
</body>

<script>
	const canvas = document.getElementById('canvas')
    const context = canvas.getContext('2d')
    // 移动原点到画布中央
    context.translate(canvas.width/2,canvas.height/2) 
    // 绘制文本 iii 和 GGG
    context.fillText('iii',0,0)
    context.fillText('GGG',0,20)
    // 测量文本 iii 和 GGG 的宽度
    const iText = context.measureText('iii')
    const GText = context.measureText('GGG')
    console.log(iText.width,GText.width)
</script>

在这里插入图片描述
上图中,我们在控制台输出了 iii 和 GGG 的宽度,显然, GGG 的宽度要大得多。

缩小文案,让文案展示成一行

我们想让内容展示成一行,可以设置如下 css 样式:

white-space: nowrap;

缩小字体,我们第一个想到可能就是直接设置 font-size,但是浏览器是有最小字体设置的,如果小于某个数值,就不会再生效。比如 Chrome 浏览器默认的最小字体为 12px,当我们设置小于 12px 的值,最终还是展示 12px 大小。当 12px 的文案还是超出了按钮宽度的时候,这个方法就失效了。

想要设置小于 12px 的字体,可以使用 transform 的 scale 来缩放,比如如下样式将元素缩小为原来的 60%:

transform: scale(0.6);

实现需求

经过上述分析之后,我们举个例子来实现需求,如下:

在这里插入图片描述
上图中,当文案翻译成英文的时候,英文内容过长,默认情况下换行展示成了两行,而需求想要的结果如下图:

在这里插入图片描述

示例代码如下:

<style>
 .container {
      display: flex;
      justify-content: center;
      align-items: center;
      flex-direction: column;
  }
  .button {
      background-color: #4CAF50;
      border: none;
      color: #fff;
      margin-bottom: 20px;
      text-decoration: none;
      display: inline-block;
      cursor: pointer;
      border-radius: 10px;
      display: flex;
      justify-content: center;
      align-items: center;

      box-sizing: border-box;
      width: 200px; // 按钮的宽度固定为 200
      height: 50px;
      white-space: nowrap; // 不换行

      font-size: 16px;
      font-family: sans-serif;
  
  }
</style>

<body>
 <div class="container">
    <button class="button">
      <span id="text">这是一个国际化翻译文案</span>
    </button>
    <button onclick="handleTranslationChange()">切换成英文</button>
  </div>
</body>

<script>
 	let buttonWidth = 200 // 按钮的固定宽度

    let handleTranslationChange = () => {
	     const text = document.getElementById('text')
	     // 文案内容改为英文
	     text.innerText = 'This is an international translation copy'
		 
		 // 创建 canvas 元素
	     const canvas = document.createElement('canvas')
	     const context = canvas.getContext('2d')
	     // font 属性值默认是 10px sans-serif
	     context.font = "16px sans-serif"
		 
		 // 调用 measureText 方法测量翻译后的
	     const measureText = context.measureText(text.innerText)
	
	     // 如果翻译之后文案宽度大于按钮,则缩小文案
	     if (measureText.width > buttonWidth) {
	       text.style.transform = 'scale(0.6)'
	     }
    }
</script>

上述 css 代码中,我们设置了按钮的样式,固定其宽度为 200px,同时设置它里面的文案不换行,字体大小为 16px。html 代码中,我们添加一个操作按钮,点击它触发 handleTranslationChange 事件,把中文翻译成英文。在handleTranslationChange 方法中,我们手动把按钮的 innerText 设置成对应的英文,模拟翻译。然后创建 canvas 元素,调用其 getContext() 的方法,这个方法是用来获得渲染上下文和它的绘画功能。

关键代码:context.font = “16px sans-serif”(默认情况下 context.font = “10px sans-serif”),即 font 要设置成和按钮文案一样,如果不设置,那么后面调用 measureText() 方法的测量宽度的时候,将会以默认值 10px的大小来测量,这样的测出的宽度实际是小于真实的文案的宽度的。

上述代码最终效果如下:

翻译前:
在这里插入图片描述

点击翻译后:
在这里插入图片描述

最后

上述实例代码中,我们是手动改变 button 的翻译的,但在实际的业务场景中,我们并不知道用户会选择什么语言,我们无法确定按钮最终的文案是什么,所以为了测量文案的宽度,我们需要使用 MutationObserver 来观察文本节点的变化,只要节点发生变化,我们就进行测量,从未决定是否缩小文案,代码调整如下:

 	let buttonWidth = 200

    let handleTranslationChange = () => {
      const text = document.getElementById('text')
      text.innerText = 'This is an international translation copy'
    }

    const text = document.getElementById('text')
    
    const observer = new MutationObserver(entries => {
      const canvas = document.createElement('canvas')
      const context = canvas.getContext('2d')
      // font 属性值默认是 10px sans-serif
      context.font = "16px sans-serif"

      const measureText = context.measureText(text.innerText)

      // 如果翻译之后文案宽度大于按钮
      if (measureText.width > buttonWidth) {
        text.style.transform = 'scale(0.6)'
      }
    })
    observer.observe(text,{ childList: true, subtree: true })

在上述代码中,handleTranslationChange() 方法要做的只是模拟用户切换语言的行为,然后其原来测量文案宽度的相关逻辑移动到 MutationObserver 的回调函数中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值