解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)
业务场景
最近碰到公司的一个业务需求: 在i18n的前提下,面对不同语言文本内容,文本长度是随着语言而变,但是包含该文本的外部容器(简单理解为div就行),是不允许改变原样式
设计
经过思考,发现transform:scale(xx)是我设计Vue组件的关键!!
通过流程图的梳理:
这里组件html部分其实很简单,我采用的就是一个容器(div标签)包含一个文本(span标签,文本内容放在span中)
<div :style="{ width: outerWidth + 'px' }">
<span>{{ textContent }}</span>
</div>
组件需要接受两个核心属性: 外部容器宽度(outerWidth) + 文本内容(textContent), 然后需要根据传入的两个核心属性进行计算渲染
而上图中的绿色方块则是我设计该组件的核心内容, please look look
这里的80px和100px没有和我流程图中的170px相对应,不好意思呢,所以主要是理解思想就行
整体的缩小实现是利用transform:scale(xx),但是缩小势必造成和外部容器的偏移,所以只需要做一个小小的计算来解决这个偏移,就可以搞定了,建议可以多看几遍上图,加深理解.
实现效果
上代码
组件代码
TextAdaptation.vue
<template>
<div class="text_adaptation" :style="{ width: outerWidth + 'px' }">
<span class="text_adaptation_inner" ref="content">{{ textContent }}</span>
</div>
</template>
<script>
export default {
name: "TextAdaptation",
props: {
// 需要自适应的文本内容
textContent: {
type: String,
default: "欢迎使用i18n文本自适应组件: TextAdaptation组件",
},
// 外部容器(最大)宽度 <单位px>, 计算时会根据屏幕(736px,可自定义)计算为vw
outerWidth: {
type: Number,
default: 100,
},
// 当使用v-show,需要重新计算
isRecalculate: {
type: Boolean,
default: true,
},
},
data() {
return {};
},
watch: {
// 监听文本内容改变
textContent() {
this.$nextTick(() => {
// 需要在DOM重新渲染后计算,否则无法获取到实时改变文本的宽度
this.init();
});
},
// 监听是否需要重新计算
isRecalculate(val) {
this.$nextTick(() => {
if (val) {
this.init();
}
});
},
},
mounted() {
this.init();
},
methods: {
init() {
// 获取当前实时文本宽度
let currentTextWidth = this.$refs.content.clientWidth;
if (this.outerWidth < currentTextWidth) {
// 获取实时伸缩比例
let proportion = this.outerWidth / currentTextWidth;
// 计算偏移距离
let marginLeftDistance = ((1 - proportion) / 2) * currentTextWidth;
console.log("文本宽度: " + currentTextWidth);
console.log("文本宽度缩小到: " + proportion);
console.log("文本向左移动距离: " + marginLeftDistance);
// 缩小文本,左移文本
this.$refs.content.style.transform = `scale(${proportion})`;
this.$refs.content.style.marginLeft = `-${marginLeftDistance}px`;
} else {
// 当 文本宽度 <= 外部容器宽度
// 复原
this.$refs.content.style.transform = `scale(1)`;
this.$refs.content.style.marginLeft = `0px`;
}
},
},
};
</script>
<style>
.text_adaptation .text_adaptation_inner {
white-space: nowrap;
/*
* 块元素宽度默认100%,继承外部为170px;
* 行内元素宽度为0;
* 行内块元素默认宽度为自身宽度
*/
display: inline-block;
}
</style>
测试演示代码
index.vue
<template>
<div>
<div class="example">
<h2>外部绿色容器宽度为: 170px</h2>
<div style="margin-top: 20px">原状态:</div>
<div class="container">
<span>适应不同语言文本长度</span>
</div>
<div style="margin-top: 20px">i18n翻译为英语, 不使用组件:</div>
<div class="container" style="white-space: nowrap">
<span>Adapt to different language text lengths</span>
</div>
<div style="margin-top: 20px">使用文本自适应组件:</div>
<div class="container" v-show="showComp">
<TextAdaptation
:textContent="contentText"
:outerWidth="170"
:isRecalculate="showComp"
></TextAdaptation>
</div>
</div>
<div @click="showComp = !showComp">点我测试消失</div>
<br />
<div @click="testTextWidthChange">点我测试文本变化</div>
</div>
</template>
<script>
import TextAdaptation from "@/components/TextAdaptation.vue";
export default {
components: {
TextAdaptation,
},
data() {
return {
showComp: true,
contentText: "Adapt to different language text lengths",
};
},
methods: {
testTextWidthChange() {
if(this.contentText === "Adapt to different language text lengths") {
this.contentText = "短文本";
}else if(this.contentText === "短文本"){
this.contentText = "Adapt to different language text lengths";
}
},
},
};
</script>
<style scoped>
.example {
height: 400px;
background: pink;
margin: 30px;
}
.example .container {
width: 170px;
height: 30px;
line-height: 30px;
background: yellowgreen;
text-align: center;
}
</style>
实际解决
其实对于i18n的不同文本适配固定宽度的适配问题,最早笔者是提出过一些常见的解决方式,例如:
- 超出的部分,用...代替,当鼠标移入后hover出内容
- 不同语言设置类似长度的简写(策划内容的事情增多,但是相比来说样式更加美观,统一; 但是并不能解决所有情况)
- 外部容器宽度随着文本变化而变化(之前是舍弃了该方案,但是后来部分地方在不影响整体美观的情况下,还是使用了,真香!)
很多市面上的使用案例
- 本文提出的方案(设计需求)
最终,我们综合的使用了2,3,4方案,看来完成好的设计,最优解往往都多个解的集合,而非单一的解法
希望能够帮到你~
参考文献:
转载自:https://juejin.cn/post/7142123963311718437