canvas绘制文本时,该如何处理首行缩进、自动换行、多内容以省略号结束、竖排的呢?

实现如标题所示的这些文本效果,在css看来,不就是一两行css属性。然而,对于canvas来讲,要想呈现这样的文字样式,就没css那么轻松简便了。

既然如此,那为何还要使用对文本支持度不友好的canvas来绘制文字呢?而不是采用对文本天然支持的css呢?

canvas 绘制文本的场景

在给为何弃对文本天然支持的 css而不用,拣起了文本支持度不友好的canvas的答复之时。不妨先来看看,究竟是在什么样的场景下,需要用到canvas去绘制文本呢?

常逛商场的,总会不时地看到一些商家为了吸引客流而制作出的一张精美海报;再看艺术展前,也总能在大门看到本次参展的“艺术家”们最为得意的艺术及简介,所绘制的一张海报;去电影院观看的,大都是被该片前期在各大商场及公共场合宣传的那张海报而吸引来的。……

是咯,照这样看来,海报的确可以作为事物的重要途径,虽然海报承载的内容有限,但一张足够优美的海报,总是能把人们的目光给吸引过来的,好比去书店买书,总是会翻到书的背面。

甭管对前沿技术的学习,抑或是为了解决问题而去搜寻资料,你总免不了在各大博客(如:秋码记录 )、论坛社区以及生活类情感博客等网站游离,那些博文、贴文等总能解决你的燃眉之急的问题,而你也会在兴奋之余随手将该文分享给朋友。

很显然,文章分享功能可以说是各大网站的基础功能,网站运营者也热衷于你把文章分享出去,从而悄无声息的向你朋友推荐了该网站。所以,网站运营者是绝不会错失这样一个向用户宣传网站媒介之一的机会。然而,社会分享组件(第三方分享:诸如 QQ、微信、微薄……)并不能很好的推介网站,基于这种状况,一张既有文章的概要,又有网站的简介的海报便应运而生了。(秋码记录 便能为文章生成一张精美的海报)。

网站生成海报的几种方案

无论是线下的,还是线上的海报,它都是一张。对于线下,想要制作出一张海报,只需将事先做好的海报图打印出来即可,但就线上而言,网站页面结构主要由htmlcssjavascript构成,而文章内容及文章封面图都是不一样的,那么,在这样的情况下,又该如何为文章生成一张海报呢?

既然知道了,海报是一张,而文章页面结构又是html,那么只需将html转变成不就成了吗?

没错,要想为文章生成海报,只需把html转成图片即可,而且还有开源第三方实现。

  • 1、html2canvas.js:从其命名上来看,一眼便能知晓,它是将页面的html转成canvas。这也是众多网站生成海报的首选之一。它的便捷在于你想要为海报生成什么款式的,你只需像修改你网站那样去修改就好。(目前 秋码记录 是采用这种方式来为文章生成海报
  • 2、dom-to-image.js:与html2canvas.js大致差不多。
  • 3、canvas:直接使用canvas绘制而生成的海报,基于wordpress的很多主题也都使用它来生成海报。( 秋码记录 即将采用这种方案)

canvas绘制文本

在使用canvas绘制文本之前,有必要了解下什么是canvas

其实,canvas也是作为html标签而存在于html结构中,而它常常被用来绘制图形及图形动画。

在现实生活中,艺术家想要画出一张画,有两样东西是必不可少的——素描纸(写生纸)、HB铅笔

而在canvas中,同样需要设置画布的大小——widthheight

canvas绘制文本API

CanvasRenderingContext2D.fillText(text, x, y [, maxWidth]);
  • texttext是需要绘制的文本。
  • xx是文本绘制的水平参考点坐标。随着CanvasRenderingContext2D.textAlign的设置不同,x的坐标位置也不同。可以表示这段文字内容左侧坐标,或水平中心坐标,或右侧坐标。
  • yy是文本绘制的垂直参考点坐标。随着CanvasRenderingContext2D.textBaseline的设置不同,y的坐标位置也不同。支持多种基线类型(CSS中也有对应概念),MDN上有一张图可以很好地表示文本基线和文本垂直位置的关系。
  • maxWidthmaxWidth表示文本内容占据的最大宽度。这里的maxWidth概念和CSS中的max-width差别很大,其最终的文本表现是:当文本占据宽度超过maxWidth的后,所有的文本自动变窄以适应这个最大宽度限制。表现类似这样:

canvas 如何实现文本首行缩进、自动换行、内容过多省略号呢?

虽然到目前为止,canvas API中还并没有提供文本首行、自动换行、内容过多以省略号结束等的支持,但还是可以通过计算X Y偏移量来实现的。

首行缩进

对于文本首行缩进,也就是在首行空出相对应的字符个数的空白位置,从而使得与其它行在视觉上达到了缩进的效果。

那么,canvas要想绘制出如css那样的首行缩进的文本样式效果,只需将X水平向右偏移相应的像素(pixel)即可。

内容过多以省略号结尾

无论是海报,还是书本的封面,总不肯多写几个字,倒像是一个惜字如命的家伙,生怕自己写多了,给自个儿带来了寿命减少几秒钟的担忧。那么,这时候,省略号这时候便闪亮登场了,很好地诠释着这一足够吸引用户眼球的重要任务。

而在canvas绘制文本时,只需判断是否绘制到文本的最后,若是,便在最后给原有文本追加上...即可。

自动换行

相对于首行缩进来讲,canvas要想实现文本自动换行,不单单只是计算X水平偏移量那么简单咯!

而还要计算Y纵向(垂直)偏移量,为了让你能够理解Y纵向偏移,我举个通俗的例子,我们平时不管是拿笔在纸上写字,还是在电子设备上敲击着文字,文本内容无不是由上而下自上而下从上而下的顺序呈现在我们的面前。由于纸或电子设备的宽度所限制,文本不得不另起一行,而这另起一行与之前的一行,就存在着纵向关系——另起一行是在之前的一行的下面(下方/ under),而之前的一行则就在另起一行的上方(上面 / upper)。

在实现自动换行这一效果时,我们应考虑到canvas该如何知道绘制当前在哪个位置(X水平位置)确需另起一行呢?这一点很重要,也是实现canvas自动换行的核心所在。

还是拿我们在纸张上写字或在电子设备上敲击文本时,我们知道换行(那是由于在纸张写的字大小差不多,一行只能容纳这么多文字)或电子设备设定好了字体大小,以此来推算出你写到哪个文字时,让你另起一行

那么,canvas实现自动换行可以通过计算文字字体大小`及纵向(Y)偏移量的。

canvas提供了CanvasRenderingContext2D.measureText(text)这个API,它可以用来计算字符的宽度。

canvas 是以左上角为原点,也就是说,X轴 水平向右是正数,反之亦然;Y 轴纵向则是以向下为正数,反向则负。

要让canvas知道该在哪个位置自动换行,只需将文本的每个字符宽度进行累加,判断总字符宽度是否达到了canvas所设定的画布宽度,若是,则另起一行继续绘制剩余的文本字符,当然咯,在另起一行时,纵向 Y偏移量是要往下移动。

 function drawMoreLines(canvas, style, content) {const ctx = canvas.getContext('2d')//从字体中解析字体大小 如:  font: '25px Helvetica'const fontHeight = parseInt(style.font.match(/\d+/), 10)//设置 canvas 文本属性ctx.font = style.fontctx.fillStyle = style.colorctx.textBaseline = 'top'ctx.textAlign = 'left'// X 轴水平偏移量 根据实际情况自行修改let alignX = 40//行宽let lineWidth = 0//文本内容截取的最后一个索引let lastSubStrIndex = 0//Y 轴 纵向偏移量let offsetY = 0//遍历文本字符for (let i = 0; i < content.length; i++) {//计算并累加每个字符的宽度lineWidth += ctx.measureText(content[i]).width;//判断累加后的字符宽度是否大于 画布宽度 减去 某个特定值(根据实际情况自行修改)if (lineWidth > canvas.width - 140) {//判断是否是首行if(lastSubStrIndex == 0 && style.textIndent){//首行 X 轴水平向右偏移 style.textIndent 个像素ctx.fillText(content.substring(lastSubStrIndex, i + 1), alignX + style.textIndent , offsetY);}else {//非首行//判断是否是最后一行if(style.ellipsis && i == content.length - 1){//将在原有文本字符末尾追加上 ...ctx.fillText(content.substring(lastSubStrIndex, i ) + '...', alignX , offsetY);}else{//按照正常的方式绘制文本字符ctx.fillText(content.substring(lastSubStrIndex, i + 1), alignX , offsetY);}}//另起一行,Y 轴偏移量是通过累加 字体大小 乘以 字符 行高offsetY += fontHeight * style.lineHeight//新的一行,将 lineWidth 重新设置为 0lineWidth = 0//文本内容截取索引设置为 另起一行 前 循环的索引lastSubStrIndex = i}}}

通过以下方式进行测试:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>秋码记录</title>
</head>
<body><canvas id="content" width="500" height="400"></canvas><script>const contentStyle = {font: '25px Helvetica',lineHeight: 1.5,position: 'left',color: 'rgba(88, 88, 88, 1)',textIndent: 40,ellipsis: true}let text = '自 秋码记录 没再为云服务器续费那会儿起,便选用了由Hugo驱动的博客主题,来迁移秋码记录 上的所有文章,然而,在众多Hugo博客主题之列,竟找不出一套属于自己想要的风格的主题,故而,也只能暂且''let $content = document.getElementById('content')drawMoreLines($content, contentStyle, text);</script></body>
</html>

在这里插入图片描述

canvas 绘制竖排文本

css中,要想实现文字竖排,只需改变writing-mode文档流的方向即可。然而只需这一行属性便可实现竖排文本的效果,使用canvas却不是那么容易的事情。

function verticalText(canvas,style,content){const ctx = canvas.getContext('2d')//设置 canvas 文本属性ctx.font = style.fontctx.fillStyle = style.colorctx.textBaseline = 'top'ctx.textAlign = 'left'// X 轴水平偏移量 根据实际情况自行修改let alignX = 40//Y 轴 纵向偏移量let offsetY = 10// 开始逐字绘制for (let i = 0; i < content.length; i++) {// 确定下一个字符的纵坐标位置let letterWidth = ctx.measureText(content[i]).width;if( i > 0){offsetY += (ctx.measureText(content[i - 1]).width) / 2}ctx.fillText(content[i], alignX , offsetY);// 旋转坐标系还原成初始态ctx.setTransform(1, 0, 0, 1, 0, 0);// 确定下一个字符的纵坐标位置letterWidth =  ctx.measureText(content[i]).width;offsetY += letterWidth;}// 水平垂直对齐方式还原ctx.textAlign = 'left';ctx.textBaseline = 'top';}
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>秋码记录</title>
</head>
<body><canvas id="vertical-text" width="100" height="300"></canvas><script>let verText = '你关注的领域,'let $varticleText = document.getElementById('vertical-text')verticalText($varticleText , contentStyle, verText);</script></body>
</html>

在这里插入图片描述

至于竖排自动换行不在本文的话题中,其实,能实现水平(X 轴)自动换行,那么 Y 轴(纵向)文本自动换行,不就是判断文字字体高度累加是否达到了canvas画布的高度,若是,则 X 轴水平向右移动(X 轴偏移量多少取决于文字的宽度及字间距`)。

还是来看看 秋码记录 为文章生成的海报(目前还是基于 html2canvas.js实现的,迟早会换掉的)

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/390933.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【开发视角】大模型 RAG 检索增强生成究竟是什么

【大白话讲懂】大模型 RAG 检索增强生成 话先说在前面&#xff0c;本文不讲不会讲太多原理&#xff0c;仅面向工程开发&#xff0c;从工作流程的宏观角度进行梳理&#xff0c;旨在快速上手。 RAG 是什么 基本定义 让我们先来解释名词&#xff0c;看看宏观框架。 RAG 的意思…

科普文:微服务之SpringBoot性能优化器动态线程池【Dynamic-Tp】特性和源码解读

一、简述 gitee地址&#xff1a;https://gitee.com/yanhom/dynamic-tp github地址&#xff1a;https://github.com/lyh200/dynamic-tp dynamic-tp是一个轻量级的动态线程池插件&#xff0c;它是一个基于配置中心的动态线程池&#xff0c;线程池的参数可以通过配置中心配置进…

C++中lambda使用mutable关键字详解

C中lambda使用mutable关键字详解 在《C初学者指南-5.标准库(第二部分)–更改元素算法》中&#xff0c;讲“generate”算法时有下面这段代码&#xff1a; auto gen [i0]() mutable { i 2; return i; }; std::vector<int> v; v.resize(7,0); generate(begin(v)1, begin…

C++ STL在算法题中的常用语法

Vector 1.将vector<int>中的元素全部置换为0 fill(vec.begin(), vec.end(), 0); 2.vector容器是可以直接用比较是否值等的&#xff01; Unordered_set 1. unordered_set的删除&#xff08;count的值也会减少&#xff09; 2.unordered_map中的int默认值是0&#xff0c;…

在Jira中使用AI

Jira已经可以使用AI功能了。 如果您使用的是Jira Cloud&#xff0c;您需要请管理员在管理页面中打开AI功能开关。&#xff08;AI功能在Standard版中未提供&#xff0c;请使用Premium或更高级的版本&#xff09;如果您使用的是自己部署的Jira Data Center&#xff0c;您需要请管…

算法学习day28

一、寻找右区间(二分法) 题意&#xff1a;题目很容易理解 但是转换为二分法有点晦涩 给你一个区间数组 intervals &#xff0c;其中 intervals[i] [starti, endi] &#xff0c;且每个 starti 都 不同 。区间 i 的 右侧区间 可以记作区间 j &#xff0c;并满足 startj > e…

OpenCV||超详细的灰度变换和直方图修正

一、点运算 概念&#xff1a;点运算&#xff08;也称为像素级运算或单像素操作&#xff09;是指对图像中每一个像素点进行独立、相同的操作&#xff0c;而这些操作不会考虑像素点之间的空间关系。点处理优势也称对比度拉伸、对比度增强或灰度变换等。 目的&#xff1a;点运算…

【EtherCAT】Windows+Visual Studio配置SOEM主站——静态库配置+部署

目录 一、准备工作 1. Visual Studio 2022 2. Npcap 1.79 3. SOEM源码 二、静态库配置 1. 修改SOEM源码配置 2. 编译SOEM源码 3. 测试 三、静态库部署 1. 新建Visual Studio工程 2. 创建文件夹 3. 创建主函数 4. 复制静态库 5. 复制头文件 6. 配置头文件…

链接、装载和库——1 简介

前言 关于个人的读书笔记 第一章 温故而知新 1.1 从hello&#xff0c;world说起 ​计算机在执行hello&#xff0c;world的时候发生了什么&#xff1f; 1.2 万变不离其宗 ​在计算机多如牛毛的硬件设备中。有三个部件最为关键&#xff0c;它们分别是 CPU、内存和 I/O 控制芯…

一次多波束和浅地层处理的经历—信标机出问题?

最近处理多波束和浅地层时&#xff0c;一个从来没有过的问题出现了。 多波束数据(.pds)是由PDS2000采集的&#xff0c;使用设备型号为T50P。浅地层数据(.raw)是有SESWIN采集的&#xff0c;使用设备型号为SES2000 Standard。 1、多波束处理 多波束数据采用CARIS11.3处理的。船…

开源LivePortrait,快速实现表情包自定义

最近可灵AI很火&#xff0c;看到网上生成的效果也很赞啊&#xff0c;之前发现快手可灵开源了LivePortrait&#xff0c;今天去玩了一下&#xff0c;很有意思。 比如下图官方展示效果&#xff1a; 这些图片开始自带表情了&#xff0c;主要就是通过LivePortrait来实现。 LivePor…

[E二叉树] lc572. 另一棵树的子树(dfs+前中序判断+树哈希+树上KMP+好题)

文章目录 1. 题目来源2. 题目解析 1. 题目来源 链接&#xff1a;572. 另一棵树的子树 2. 题目解析 看到这个题目就感觉不简单&#xff0c;因为写了写 dfs 版本的&#xff0c;发现好像不太会… 还是简单粗暴一点&#xff0c;直接搞一个 前序中序&#xff0c;进行判断即可。我…

【PyTorch】神经风格迁移项目

神经风格迁移中&#xff0c;取一个内容图像和一个风格图像&#xff0c;综合内容图像的内容和风格图像的艺术风格生成新的图像。 目录 准备数据 处理数据 神经风格迁移模型 加载预训练模型 定义损失函数 定义优化器 运行模型 准备数据 创建data文件夹&#xff0c;放入…

数据恢复软件:电脑丢失文件,及时使用数据恢复软件恢复!

数据恢复软件什么时候会用到&#xff1f; 答&#xff1a;如果真的不小心删除文件&#xff0c;清空回收站&#xff0c;电脑重装系统等情况发生&#xff0c;我们要懂的及时停止使用电子设备&#xff0c;使用可靠的数据恢复软件&#xff0c;帮助我们恢复这些电子设备的数据&#…

二进制搭建 Kubernetes v1.20(上)

目录 一、操作系统初始化配置 二、升级Liunx内核 三、部署docker引擎 四、部署etcd集群 五、部署Master组件 六、部署Worker Node组件 hostnameip需要部署k8s集群master0120.0.0.100kube-apiserver kube-controller-manager kube-scheduler etcdk8s集群master0220.0.0.1…

CookieMaker工作室合作开发C++项目十一:拟态病毒

&#xff08;注&#xff1a;本文章使用了“无标题技术”&#xff09; 一天&#xff0c;我和几个同事&#xff0c;平台出了点BUG&#xff0c;居然给我刷出了千年杀&#xff0c;同事看得瑕疵欲裂&#xff0c;发誓要将我挫骨扬灰—— &#xff08;游戏入口&#xff1a;和平精英31.…

【数据脱敏】数据交换平台数据脱敏建设方案

1 概述 1.1 数据脱敏定义 1.2 数据脱敏原则 1.2.1基本原则 1.2.2技术原则 1.2.3管理原则 1.3 数据脱敏常用方法 3.1.1泛化技术 3.1.2抑制技术 3.1.3扰乱技术 3.1.4有损技术 1.4 数据脱敏全生命周期 2 制定数据脱敏规程 3 发现敏感数据 4 定义脱敏规则 5 执…

[Unity] ShaderGraph实现DeBuff污染 溶解叠加效果

本篇是在之前的基础上&#xff0c;继续做的功能衍生。 [Unity] ShaderGraph实现Sprite消散及受击变色 完整连连看如下所示&#xff1a;

TypeError: ‘float’ object is not iterable 深度解析

TypeError: ‘float’ object is not iterable 深度解析与实战指南 在Python编程中&#xff0c;TypeError: float object is not iterable是一个常见的错误&#xff0c;通常发生在尝试对浮点数&#xff08;float&#xff09;进行迭代操作时。这个错误表明代码中存在类型使用不…

C基础项目(学生成绩管理系统)

目录 一、项目要求 二、完整代码实例 三、分文件编写代码实例 一、项目要求 1.系统运行&#xff0c;打开如下界面。列出系统帮助菜单&#xff08;即命令菜单&#xff09;&#xff0c;提示输入命令 2.开始时还没有录入成绩&#xff0c;所以输入命令 L 也无法列出成绩。应提…