目前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去掉後,看下效果:
OK,可以看到,佔高沒有按我們想的“被縮放”。影響到了下面的元素位置。
本質上是“視覺大小改變了但實際大小無變化”。
這一點需要注意,一般來説,給被縮放元素顯式設置一個大於等於其font-size的高度值即可。
縮放帶來的其它問題
可能在很多人使用的場景中是不會考慮到這個問題的:被縮放元素限制高度以後如果元素換行那麼會出現文字重疊的現象。
為此,我採用了在mounted生命週期中獲取父元素寬度,然後動態計算是否需要換行以及換行的行數,最後用動態style重新渲染每一條數據的height值。
這裏有三點需要注意:
- 這裏用的是一種取巧的方法:用
每個文字的視覺font-size值*字符串長度。因為筆者遇到的場景不會出現問題所以可以這麼用。在不確定場景中更推薦用canvas或dom實際計算每個字符的寬度再做判斷(需要知道文字、字母和數字的寬度是不一樣的); - 需要注意一些特殊機型的展示,比如三星的galaxy fold,這玩意是個摺疊屏,它的計算會和一般的屏幕計算的不一致;
- 在vue生命週期中,mounted可以操作dom,但不能獲取實際dom元素;你可以通過
this.$el獲取元素。但要注意:在這個時期被獲取的元素不能用v-if(即:必須存在於虛擬tree中)。這也是上面代碼中筆者使用v-show和opacity的原因。
關於第三點,這裏有個時機問題。比如剛進入頁面時要展示彈窗,彈窗是一個組件。那你在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;
},