需求背景
echarts x轴标签超出两行后显示...
需求分析
1.配置项不能使用html模板,因此不能通过css去实现
2.文字、字母、数字符号占位宽度不一样,也不能简单通过字符长度去计算要显示的内容
效果展示
实现方案
通过canvas中context.measureText()方法准确计算文字的占位宽度
代码解释
1.测量文本宽度核心方法
// 返回一个能够计算文本像素的方法 防止重复创建canvas
const useContext = (fontStyle) => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
context.font = fontStyle
const getTextWidth = (text) => {
const width = context.measureText(text).width
return width
}
return getTextWidth
}
const getTextWidth = useContext(fontStyle)
2.给定文字和宽度 计算最后一个不能完整显示字符的索引
// 计算一段文本能够容纳文本的最后一个索引
const getClipIndex = (text, maxWidth) => {
const textLenth = text.length
const width = getTextWidth(text)
if (width <= maxWidth) {
return -1 // 没有超出
}
if (maxWidth < fontSize) {
return 0 // 给定宽度比字号还小 不计算
}
let resultIndex // 查找停止的表示 返回出去的索引
const indexs = [0, textLenth - 1] //被切断文字的索引范围
while (!resultIndex) {
const index = indexs[0] + Math.floor((indexs[1] - indexs[0]) / 2) //二分法中点
const currentW = getTextWidth(text.slice(0, index + 1)) // 中点文字开始像素
const lastW = getTextWidth(text.slice(0, index)) //中点文字结束像素
if (currentW > maxWidth && lastW <= maxWidth) { // 给定宽度在文字所占像素范围内
resultIndex = index //第一个不能完整展示的文字索引
} else if (lastW > maxWidth && currentW > maxWidth) { //中点文字在给定宽度右侧(之外)
indexs[1] = index
} else if (lastW < maxWidth && currentW < maxWidth) { // 中点文字在给定宽度左侧(需往右查找)
indexs[0] = index + 1
}
}
return resultIndex
}
3.用...代替隐藏的文本,也可以换成其他字符代替
// 用...代替被隐藏的文字
const toEllipsis = (text) => {
const threeDotWidth = getTextWidth("...")
let letterNum = 1
while (threeDotWidth > getTextWidth(text.slice(-letterNum))) {
letterNum++
}
return text.slice(0, -letterNum) + "..."
}
4.返回实际显示文本,可设定行数
// 给出行宽和最大行数 输出要展示的文本
const getLastClipByRow = (text, rows, maxWidth) => {
let startIndex = 0
for (let index = 0; index < rows; index++) {
const rowText = text.slice(startIndex)
const currentRowLastIndex = getClipIndex(rowText, maxWidth)
if (currentRowLastIndex > 0) {
startIndex += currentRowLastIndex
} else {
break
}
}
if (startIndex > text.lenth) {
return text
} else {
}
const rowsText = text.slice(0, startIndex)
return toEllipsis(rowsText)
}
5.整体demo代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<style>
.text1,
.text2 {
font-size: 14px;
width: fit-content;
max-width: 100px;
word-break: break-all;
background-color: greenyellow;
}
.text1 {
display: -webkit-box;
text-overflow: ellipsis;
overflow: hidden;
-webkit-box-orient: vertical;
-webkit-line-clamp: 3;
}
</style>
<body>
<div id="app">
<h3>使用css样式的超出隐藏</h3>
<div class="text1">{{testText}}</div>
<br>
<h3>计算出来不用css样式的超出隐藏</h3>
<div class="text2">{{showLetters}}</div>
</div>
<script>
const { createApp, onMounted, ref } = Vue
const app = createApp({
setup() {
const testText = "一a跨1(2境d电商m房,间|爱a了快点)解封价一a跨1(2境d电商m房,间|爱间啊"
const fontSize = 14
const fontStyle = `${fontSize}px Microsoft YaHei`
// 返回一个能够计算文本像素的方法 防止重复创建canvas
const useContext = (fontStyle) => {
const canvas = document.createElement('canvas')
const context = canvas.getContext('2d')
context.font = fontStyle
const getTextWidth = (text) => {
const width = context.measureText(text).width
return width
}
return getTextWidth
}
const getTextWidth = useContext(fontStyle)
// 计算一段文本能够容纳文本的最后一个索引
const getClipIndex = (text, maxWidth) => {
const textLenth = text.length
const width = getTextWidth(text)
if (width <= maxWidth) {
return -1 // 没有超出
}
if (maxWidth < fontSize) {
return 0 // 给定宽度比字号还小 不计算
}
let resultIndex // 查找停止的表示 返回出去的索引
const indexs = [0, textLenth - 1] //被切断文字的索引范围
while (!resultIndex) {
const index = indexs[0] + Math.floor((indexs[1] - indexs[0]) / 2) //二分法中点
const currentW = getTextWidth(text.slice(0, index + 1)) // 中点文字开始像素
const lastW = getTextWidth(text.slice(0, index)) //中点文字结束像素
if (currentW > maxWidth && lastW <= maxWidth) { // 给定宽度在文字所占像素范围内
resultIndex = index //第一个不能完整展示的文字索引
} else if (lastW > maxWidth && currentW > maxWidth) { //中点文字在给定宽度右侧(之外)
indexs[1] = index
} else if (lastW < maxWidth && currentW < maxWidth) { // 中点文字在给定宽度左侧(需往右查找)
indexs[0] = index + 1
}
}
return resultIndex
}
// 用...代替被隐藏的文字
const toEllipsis = (text) => {
const threeDotWidth = getTextWidth("...")
let letterNum = 1
while (threeDotWidth > getTextWidth(text.slice(-letterNum))) {
letterNum++
}
return text.slice(0, -letterNum) + "..."
}
// 给出行宽和最大行数 输出要展示的文本
const getLastClipByRow = (text, rows, maxWidth) => {
let startIndex = 0
for (let index = 0; index < rows; index++) {
const rowText = text.slice(startIndex)
const currentRowLastIndex = getClipIndex(rowText, maxWidth)
if (currentRowLastIndex > 0) {
startIndex += currentRowLastIndex
} else {
break
}
}
if (startIndex > text.lenth) {
return text
} else {
}
const rowsText = text.slice(0, startIndex)
return toEllipsis(rowsText)
}
const showLetters = ref('')
onMounted(() => {
showLetters.value = getLastClipByRow(testText, 3, 100)
})
return {
testText,
showLetters
}
}
})
app.mount("#app")
</script>
</body>
</html>