likes
comments
collection

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

作者站长头像
站长
· 阅读数 16

业务场景

最近碰到公司的一个业务需求: 在i18n的前提下,面对不同语言文本内容,文本长度是随着语言而变,但是包含该文本的外部容器(简单理解为div就行),是不允许改变原样式

设计

经过思考,发现transform:scale(xx)是我设计Vue组件的关键!!

通过流程图的梳理:

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

这里组件html部分其实很简单,我采用的就是一个容器(div标签)包含一个文本(span标签,文本内容放在span中)

<div :style="{ width: outerWidth + 'px' }">
    <span>{{ textContent }}</span>
</div>

组件需要接受两个核心属性: 外部容器宽度(outerWidth) + 文本内容(textContent), 然后需要根据传入的两个核心属性进行计算渲染

而上图中的绿色方块则是我设计该组件的核心内容, please look look

这里的80px和100px没有和我流程图中的170px相对应,不好意思呢,所以主要是理解思想就行

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

整体的缩小实现是利用transform:scale(xx),但是缩小势必造成和外部容器的偏移,所以只需要做一个小小的计算来解决这个偏移,就可以搞定了,建议可以多看几遍上图,加深理解.

实现效果

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

上代码

组件代码

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的不同文本适配固定宽度的适配问题,最早笔者是提出过一些常见的解决方式,例如:

  1. 超出的部分,用...代替,当鼠标移入后hover出内容
  2. 不同语言设置类似长度的简写(策划内容的事情增多,但是相比来说样式更加美观,统一; 但是并不能解决所有情况)
  3. 外部容器宽度随着文本变化而变化(之前是舍弃了该方案,但是后来部分地方在不影响整体美观的情况下,还是使用了,真香!)

很多市面上的使用案例

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

解决i18n不同语言文本长度对固定背景宽度的适配问题(Vue组件)

  1. 本文提出的方案(设计需求)

最终,我们综合的使用了2,3,4方案,看来完成好的设计,最优解往往都多个解的集合,而非单一的解法

希望能够帮到你~

参考文献:

juejin.cn/post/709199…

juejin.cn/post/713173…