最簡單粗暴的js方式實現background-size

最近有個需求,要求按照最小邊展現圖片,而且要求同一張圖片,在客戶端側和前端側展現的圖片樣式徹底相同。
這就要求前端跟客戶端的圖片大小要徹底一致,而且雙方協商好按照同一方式進行展現。css

固然,咱們能夠直接用background-size:cover;來解決,可是,background是畢竟不是img,若是想寬度固定,高度自適應仍是比較難的,而直接用img,那麼只能使用object-fit:cover;object-position:center;的方法,可是這種方法兼容性較差。 html

object-fit兼容性

object-fit 兼容性前端

因此,這裏咱們探討一些其餘的方法。git

咱們可讓div套住img,經過div限制大小,經過獲取img的尺寸自適應div。github

<div class="img_box">
    <img class="img" />
</div>
複製代碼

思路肯定

以下圖所示,左側空白正方形爲一個box,右側矩形爲圖片。canvas

  • 步驟一:新建一個box和一個圖片;
  • 步驟二:可將圖片最小邊縮放至與box一樣大小,而後將圖片大小同比例縮放;
  • 步驟三:將圖片移動至box中間位置。
    步驟1
    步驟2
    步驟3

問題分解

  • 問題1:根據寬高比例不一樣,須要進行不一樣的縮放,如何獲得圖片的寬與高呢?
  • 答:圖片在不加載以前,是沒法獲取圖片具體的尺寸的,那麼就讓圖片先加載出來,先得到圖片的尺寸,再根據圖片尺寸類型進行展現。
let imgLoader = new Image();
let img = document.querySelector('.img'), box = document.querySelector('.img_box');
let imgSrc = './pic1.jpg';
imgLoader.onload = () => {
    let width = imgLoader.width, height = imgLoader.height;
}
imgLoader.src = imgSrc;
複製代碼

注意:要把imgLoader掛載src的地方放在onload之後,以防止有緩存的狀況下onload事件不執行。api

  • 問題2:如何計算圖片展現的起點及圖片應該展現的寬度?
  • 答:在此類狀況下,能夠直接定位到中間部分。
<style>
    .img_box{
        width: 300px;
        height: 200px;
        background: black;
        position: relative;
        overflow: hidden;
    }
    .img{
        position: absolute;
    }
</style>
<script>
    let imgLoader = new Image();
    let img = document.querySelector('.img'), box = document.querySelector('.img_box');
    let imgSrc = './pic1.jpg';
    imgLoader.onload = () => {
        let width = imgLoader.width, height = imgLoader.height;
        if(width > height){
            img.style.height = '100%';
            img.style.left = '50%';
            img.style.transform = 'translateX(-50%)';
        }else if(width < height){
            img.style.width = '100%';
            img.style.top = '50%';
            img.style.transform = 'translateY(-50%)';
        }
        img.src = imgSrc;
    }
    imgLoader.src = imgSrc;
</script>
複製代碼
  • 問題3:若是圖片的寬高相同且box爲寬大於高該怎麼展現?
  • 答:當box的寬大於高,此時不可將圖片的高度設置爲與box相同,不然展現的樣式會與最大邊展現相同,以下圖所示。
    box寬度大於高度
    將img高度設置爲與box高度相同
    所以,此種狀況,圖片的高度應自適應,寬度與box同寬,即該類狀況與圖片的寬度小於圖片的高度相同。
let imgLoader = new Image();
let img = document.querySelector('.img'), box = document.querySelector('.img_box');
let imgSrc = './pic1.jpg', style = 'cover';
imgLoader.onload = () => {
    let width = imgLoader.width, height = imgLoader.height;
    if(width > height || (width == height && box.offsetWidth < box.offsetHeight)){
        img.style.height = '100%';
        img.style.left = '50%';
        img.style.transform = 'translateX(-50%)';
    }else if(width < height || (width == height && box.offsetWidth > box.offsetHeight)){
        img.style.width = '100%';
        img.style.top = '50%';
        img.style.transform = 'translateY(-50%)';
    }
    img.src = imgSrc;
}
imgLoader.src = imgSrc;
複製代碼

一樣的道理,若是以最大邊做爲展現的依據,則把兩個規則調轉一下便可,並增長一個選項。瀏覽器

let imgLoader = new Image();
    let img = document.querySelector('.img'),box = document.querySelector('.img_box');
    let imgSrc = './pic2.jpg',style = 'cover';
    imgLoader.onload = () => {
        let width = imgLoader.width, height = imgLoader.height;
        if(style == 'cover'){
            if(width > height || (width == height && box.offsetWidth < box.offsetHeight)){
                img.style.height = '100%';
                img.style.left = '50%';
                img.style.transform = 'translateX(-50%)';
            }else if(width < height || (width == height && box.offsetWidth > box.offsetHeight)){
                img.style.width = '100%';
                img.style.top = '50%';
                img.style.transform = 'translateY(-50%)';
            }else{
                img.style.width = '100%';
                img.style.height = '100%';
            }
        }else if(style == 'contain'){
            if(width > height || (width == height && box.offsetWidth < box.offsetHeight)){
                img.style.width = '100%';
                img.style.top = '50%';
                img.style.transform = 'translateY(-50%)';
            }else if(width < height || (width == height && box.offsetWidth > box.offsetHeight)){
                img.style.height = '100%';
                img.style.left = '50%';
                img.style.transform = 'translateX(-50%)';
            }else{
                img.style.width = '100%';
                img.style.height = '100%';
            }
        }
        img.src = imgSrc;
    } 
    imgLoader.src = imgSrc;
複製代碼

這裏按照background-size的方法,增長style參數,若是style='cover',表示按最小邊展現,若是style='cover',表示按最大邊展現。緩存

  • 問題4:若是按照最小邊展現時,同比例壓縮後的圖片的寬小於box的寬怎麼辦?
  • 答:有一種特殊狀況,圖片的寬大於高,可是同比例壓縮後,圖片的寬度仍然小於box的寬度,以下圖所示。
    異常狀況
    異常狀況展現
    所以,這裏咱們不該該使用圖片的寬與高大小的差異來判斷如何展現,而應該經過對圖片與box的寬高比進行展現的判斷。
let imgLoader = new Image();
    let img = document.querySelector('.img'),box = document.querySelector('.img_box');
    let imgSrc = './pic2.jpg',style = 'cover';
    imgLoader.onload = () => {
        let width = imgLoader.width, height = imgLoader.height;
        if(style == 'cover'){
            if(width/height > box.offsetWidth/box.offsetHeight){
                img.style.height = '100%';
                img.style.left = '50%';
                img.style.transform = 'translateX(-50%)';
            }else if(width/height < box.offsetWidth/box.offsetHeight){
                img.style.width = '100%';
                img.style.top = '50%';
                img.style.transform = 'translateY(-50%)';
            }else{
                img.style.width = '100%';
                img.style.height = '100%';
            }
        }else if(style == 'contain'){
            if(width/height > box.offsetWidth/box.offsetHeight){
                img.style.width = '100%';
                img.style.top = '50%';
                img.style.transform = 'translateY(-50%)';
            }else if(width/height < box.offsetWidth/box.offsetHeight){
                img.style.height = '100%';
                img.style.left = '50%';
                img.style.transform = 'translateX(-50%)';
            }else{
                img.style.width = '100%';
                img.style.height = '100%';
            }
        }
        img.src = imgSrc;
    } 
    imgLoader.src = imgSrc;
複製代碼

img寬>img高 bash

原圖
方法一寬>高cover
方法一寬>高contain
方法一寬<高cover
方法一寬<高contain
img寬>img高
原圖
方法一寬>高cover
方法一寬>高contain
方法一寬<高cover
方法一寬<高contain
img寬=img高
原圖
方法一寬>高cover
方法一寬>高contain
方法一寬<高cover
方法一寬<高contain
上面的一些圖片,左側是用方法一作到的效果,而右側是用background-size:cover/contain達到的效果,能夠看出,徹底一致。

優化尺寸獲取方式

可是上面的方法本質上是在加載過一次圖片後,獲取了寬高再進行調整。

雖然一般狀況下,瀏覽器的資源緩存機制會使圖像會只加載一次並在整個會話中使用,可是new Image()這種方法一般用做預加載,在此處咱們不須要進行預加載,所以咱們能夠作這樣的修改。

let img = box.getElementsByTagName('img')[0];
img.onload = () =>{
    let width = img.offsetWidth, height = img.offsetHeight;
    if(type == 'cover'){
        if(width > height || (width == height && box.offsetWidth < box.offsetHeight)){
            img.style.height = '100%';
            img.style.left = '50%';
            img.style.transform = 'translateX(-50%)';
        }else if(width < height || (width == height && box.offsetWidth > box.offsetHeight)){
            img.style.width = '100%';
            img.style.top = '50%';
            img.style.transform = 'translateY(-50%)';
        }else{
            img.style.width = '100%';
            img.style.height = '100%';
        }
    }else if(type == 'contain'){
        if(width > height || (width == height && box.offsetWidth < box.offsetHeight)){
            img.style.width = '100%';
            img.style.top = '50%';
            img.style.transform = 'translateY(-50%)';
        }else if(width < height || (width == height && box.offsetWidth > box.offsetHeight)){
            img.style.height = '100%';
            img.style.left = '50%';
            img.style.transform = 'translateX(-50%)';
        }else{
            img.style.width = '100%';
            img.style.height = '100%';
        }
    }
}
複製代碼

上面這種方式經過直接加載圖片,而後獲取圖片的尺寸,再根據尺寸動態的進行修改圖片的 展現。一樣,也能夠建立一次實例而後利用canvas加載圖像的方法來展現。

let canvas =document.getElementById("canvas"),width = 300,height = 180,imgSrc = './pic2.jpg',style = 'contain';
// 設置寬高經過js來控制,不要用css
canvas.width = width;
canvas.height = height;
let ctx = canvas.getContext("2d");
ctx.fillStyle = '#000000';
ctx.fillRect(0, 0, width, height);
let img = new Image();
img.onload = ()=>{ 
    let {startX, startY, lengthWidth, lengthHeight} = setSize(style,width,height,img.width,img.height);
    ctx.drawImage(img, startX, startY, lengthWidth, lengthHeight, 0, 0, width, height); 
}
img.src = imgSrc;
/*
 * 設置圖片的展現方式
 * @param {Number} style 肯定取最小邊仍是最長邊, box_width 固定盒子的寬, box_height 固定盒子的高
 * @param {Number} source_width 原圖片的寬, source_height 原圖片的高
 * @return {Object} {截取的圖片信息},對應drawImage(imageResource, startX, startY, lengthWidth, lengthHeight, dx, dy, dWidth, dHeight)參數
 */
 function setSize(style,box_width, box_height, source_width, source_height){
    let startX = 0,startY = 0,lengthWidth = source_width,lengthHeight = source_height;
    if(style == 'cover'){
        if(source_width > source_height || (source_width == source_height && box_width < box_height)){
            lengthWidth = box_width*lengthHeight/box_height;
            startX = (source_width - lengthWidth)/2;
        }else if(source_width < source_height || (source_width == source_height && box_width > box_height)){
            lengthHeight = box_height*lengthWidth/box_width;
            startY = (source_height-lengthHeight)/2;
        }else{
            lengthWidth = box_width;
            lengthWidth = box_height;
        }
    }else if(style == "contain"){
        if(source_width > source_height || (source_width == source_height && box_width < box_height)){
            lengthHeight = box_height*lengthWidth/box_width;
            startY = (source_height-lengthHeight)/2;
        }else if(source_width < source_height || (source_width == source_height && box_width > box_height)){
            lengthWidth = box_width*lengthHeight/box_height;
            startX = (source_width - lengthWidth)/2;
        }else{
            lengthWidth = box_width;
            lengthWidth = box_height;
        }
    }
    return {startX,startY,lengthWidth,lengthHeight}
}
複製代碼

在寫canvas時請注意,canvas的寬高請使用js來調整,而不要修改css。

簡單理解,canvas原理就是一個畫畫的原理,canvas這個標籤是畫板,而在canvas中展現的東西是在畫布上的。在渲染canvas時,初始化畫板爲300*150,一樣畫布也是300*150。當經過改變canvas的style進行寬高的修改時,其實僅僅是修改畫板的寬高,而當畫板與畫紙尺寸不統一時,畫紙內的圖像就會趨向於與畫板的尺寸相同,必然會致使圖像的拉伸或者壓縮。

這裏能夠參考該文章

所以,在修改尺寸的時候,要同步修改畫板與畫紙,這裏能夠直接使用用HTML 5 canvas 的api動態控制。

總結

以上三種方法,均可以獲得與background-size:cover;background-position:center;object-fit:cover;object-position:center;相同的效果。各位也能夠根據本身的須要調整max-heighttransform等屬性。

這裏提供下demo,地址可見:github.com/tingchow/se…

若有問題,歡迎提出修改。

相關文章
相關標籤/搜索