博客 / 詳情

返回

如何突破瀏覽器12px限制

目前Chrome瀏覽器依然沒有放開12px的限制,但Chrome仍然是使用人數最多的瀏覽器。

在筆者開發某個項目時突發奇想:如果實際需要11px的字體大小怎麼辦?這在Chrome中是實現不了的。關於字體,一開始想到的就是rem等非px單位。但是rem只是為了響應式適配,並不能突破這一限制。

em、rem等單位只是為了不同分辨率下展示效果提出的換算單位,常見的庫px2rem也只是利用了js將px轉為rem。包括微信小程序提出的rpx單位也是一樣!

這條路走不通,就只剩下一個方法:改變視覺大小而非實際大小

理論基礎

css中有一個屬性:transform: scale();

  • 值的絕對值>1,就是放大,比如2,就是放大2倍
  • 值的絕對值 0<值<1,就是縮小,比如0.5,就是原來的0.5倍;
  • 值的正負,負值表示圖形翻轉。

默認情況下,scale(x, y):以x/y軸進行縮放;如果y沒有值,默認y==x
也可以分開寫:scaleX() scaleY() scaleZ(),分開寫的時候,可以對Z軸進行縮放

第二種寫法:transform: scale3d(x, y, z)該寫法是上面的方法的複合寫法,結果和上面的一樣。

但使用這個屬性要注意一點:scale 縮放的時候是以“縮放元素所在空間的中心點”為基準的。
所以如果用在改變元素視覺大小的場景下,一般還需要利用另一個元素來“恢復位置”:

transform-origin: top left;

語法上説,transform-origin 擁有三個屬性值:

transform-origin: x-axis y-axis z-axis;

默認為:

transform-origin:50% 50% 0;

屬性值可以是百分比、em、px等具體的值,也可以是top、right、bottom、left和center這樣的關鍵詞。作用就是更改一個元素變形的原點。

實際應用

<div class="mmcce__info-r">
  <!-- 一些html結構 -->
  <div v-show="xxx" class="mmcce-valid-mj-period" :class="{'mmcce-mh': showStr}" @click="handleShowStr"> <!-- click中事件控制展示與否 -->
    <div class="mmcce-valid-period-child">{{couponInfo.startTimeFormat}}-{{couponInfo.endTimeFormat}}</div><!-- 父級結構,點擊顯示下面內容 -->
    <div class="mmcce-valid-pro" ref="mmcceW" :style="{opacity: showStr ? 1 : 0}">
      <!-- 下面內容在後面有講解 -->
      <div class="mmcce-text" 
        v-for="(item, index) in couponInfo.thresholdStr" 
        :key="index" 
        :index="index"
        :style="{height: mTextH[index] + 'px'}"
      >{{item}}</div>
    </div>
  </div>
</div>
.mmcce-valid-mj-period {
  max-height: 15px;
  transition: all .2s ease;

  &.mmcce-mh {
    max-height: 200px;
  }

  .mmcce-valid-pro {
    display: flex;
    flex-direction: column;
    padding-bottom: 12px;

    .mmcce-text {
      width: 200%;
      font-size: 22px;
      height: 15px;
      line-height: 30px;
      color: #737373;
      letter-spacing: 0;
      transform       : scale(.5);
      transform-origin: top left;
    }
  }
}

.mmcce-valid-period-child {
  position: relative;
  width      : 200%;
  white-space: nowrap;
  font-size  : 22px;
  color      : #979797;
  line-height: 30px;

  transform       : scale(.5);
  transform-origin: top left;

  //xxx
}

微店-卡券包項目截圖

可以明確説明的是,這樣的 hack 需要明確規定縮放元素的height值!

上面代碼中為什麼.mmcce-valid-mj-period類中要用max-height ?為什麼對展開元素中的文字類.mmcce-text中使用height
我將類.mmcce-text中的height去掉後,看下效果:
微店-卡券包項目截圖2

OK,可以看到,佔高沒有按我們想的“被縮放”。影響到了下面的元素位置。

本質上是“視覺大小改變了但實際大小無變化”。

這一點需要注意,一般來説,給被縮放元素顯式設置一個大於等於其font-size的高度值即可。

縮放帶來的其它問題

可能在很多人使用的場景中是不會考慮到這個問題的:被縮放元素限制高度以後如果元素換行那麼會出現文字重疊的現象。
微店-卡券包項目截圖3

為此,我採用了在mounted生命週期中獲取父元素寬度,然後動態計算是否需要換行以及換行的行數,最後用動態style重新渲染每一條數據的height值。
這裏有三點需要注意:

  1. 這裏用的是一種取巧的方法:用每個文字的視覺font-size值*字符串長度。因為筆者遇到的場景不會出現問題所以可以這麼用。在不確定場景中更推薦用canvas或dom實際計算每個字符的寬度再做判斷(需要知道文字、字母和數字的寬度是不一樣的);
  2. 需要注意一些特殊機型的展示,比如三星的galaxy fold,這玩意是個摺疊屏,它的計算會和一般的屏幕計算的不一致;
  3. 在vue生命週期中,mounted可以操作dom,但不能獲取實際dom元素;你可以通過this.$el獲取元素。但要注意:在這個時期被獲取的元素不能用v-if(即:必須存在於虛擬tree中)。這也是上面代碼中筆者使用v-showopacity的原因。
關於第三點,這裏有個時機問題。比如剛進入頁面時要展示彈窗,彈窗是一個組件。那你在index.vue中是獲取不到這個組件的。(這個和v-if還是v-show沒有關係)但是你可以將比如header也拆分出來,然後在header組件的mounted中去調用彈窗組件暴露出的方法。
mounted(){
  let thresholdStr = this.info.dropDownTextList;
  let minW = false;
  if(this.$el.querySelector('.mmcce-valid-pro').clientWidth < 140) { // 以iPhone5位準,再小於其中元素寬度的的機型就要做特殊處理了
    minW = true
  }
  let mmcw = this.$el.querySelector('.mmcce-valid-pro').getBoundingClientRect().width;

  let mmch = [];

  for(let i=0;i<thresholdStr.length;i++) {
    // 11是指縮放後文字的font-size值,這是一種取巧的方式
    if(11*(thresholdStr[i].length) > mmcw) {
      if(minW) {
        mmch[i] = Math.floor((11*thresholdStr[i].length) / mmcw) * 15;
      }else {
        mmch[i] = Math.floor((11*(thresholdStr[i].length) + 40) / mmcw) * 15;
      }
    }else {
      mmch[i] = 15;
    }
  }

  this.mTextH = mmch;
},
user avatar
0 位用戶收藏了這個故事!

發佈 評論

Some HTML is okay.