從0到1搭建一款頁(yè)面自適應(yīng)組件(Vue.js)
組件將根據(jù)屏幕比例及當(dāng)前瀏覽器窗口大小,自動(dòng)進(jìn)行縮放處理。
建議在組件內(nèi)使用百分比搭配flex進(jìn)行布局,以便于在不同的分辨率下得到較為一致的展示效果。
使用前請(qǐng)注意將body的margin設(shè)為0,否則會(huì)引起計(jì)算誤差。
fullScreenContainer.vue
<template>
<div id="full-screen-container" :ref="ref">
<template v-if="ready">
<slot></slot>
</template>
</div>
</template>
<script>
import autoResize from './autoResize.js'
export default {
name: 'DvFullScreenContainer',
mixins: [autoResize],
data () {
return {
ref: 'full-screen-container',
allWidth: 0,
scale: 0,
datavRoot: '',
ready: false
}
},
methods: {
afterAutoResizeMixinInit () {
const { initConfig, setAppScale } = this
initConfig()
setAppScale()
this.ready = true
},
initConfig () {
const { dom } = this
const { width, height } = screen
this.allWidth = width
dom.style.width = `${width}px`
dom.style.height = `${height}px`
},
setAppScale () {
const { allWidth, dom } = this
const currentWidth = document.body.clientWidth
dom.style.transform = `scale(${currentWidth / allWidth})`
},
onResize () {
const { setAppScale } = this
setAppScale()
}
}
}
</script>
<style lang="scss">
#full-screen-container {
position: fixed;
top: 0px;
left: 0px;
overflow: hidden;
transform-origin: left top;
z-index: 999;
}
</style>
autoResize.js
export default {
data() {
return {
dom: '',
width: 0,
height: 0,
debounceInitWHFun: '',
domObserver: ''
};
},
methods: {
debounce(delay, callback) {
let lastTime;
return function() {
clearTimeout(lastTime);
const [that, args] = [this, arguments];
lastTime = setTimeout(() => {
callback.apply(that, args);
}, delay);
};
},
observerDomResize(dom, callback) {
const MutationObserver =
window.MutationObserver ||
window.WebKitMutationObserver ||
window.MozMutationObserver;
const observer = new MutationObserver(callback);
observer.observe(dom, {
attributes: true,
attributeFilter: ['style'],
attributeOldValue: true
});
return observer;
},
async autoResizeMixinInit() {
const {
initWH,
getDebounceInitWHFun,
bindDomResizeCallback,
afterAutoResizeMixinInit
} = this;
await initWH(false);
getDebounceInitWHFun();
bindDomResizeCallback();
if (typeof afterAutoResizeMixinInit === 'function')
afterAutoResizeMixinInit();
},
initWH(resize = true) {
const { $nextTick, $refs, ref, onResize } = this;
return new Promise(resolve => {
$nextTick(() => {
const dom = (this.dom = $refs[ref]);
this.width = dom ? dom.clientWidth : 0;
this.height = dom ? dom.clientHeight : 0;
if (!dom) {
console.warn(
'DataV: Failed to get dom node, component rendering may be abnormal!'
);
} else if (!this.width || !this.height) {
console.warn(
'DataV: Component width or height is 0px, rendering abnormality may occur!'
);
}
if (typeof onResize === 'function' && resize) onResize();
resolve();
});
});
},
getDebounceInitWHFun() {
const { initWH } = this;
this.debounceInitWHFun = this.debounce(100, initWH);
},
bindDomResizeCallback() {
const { dom, debounceInitWHFun } = this;
this.domObserver = this.observerDomResize(dom, debounceInitWHFun);
window.addEventListener('resize', debounceInitWHFun);
},
unbindDomResizeCallback() {
let { domObserver, debounceInitWHFun } = this;
if (!domObserver) return;
domObserver.disconnect();
domObserver.takeRecords();
domObserver = null;
window.removeEventListener('resize', debounceInitWHFun);
}
},
mounted() {
const { autoResizeMixinInit } = this;
autoResizeMixinInit();
},
beforeDestroy() {
const { unbindDomResizeCallback } = this;
unbindDomResizeCallback();
}
};
這樣,一個(gè)頁(yè)面自適應(yīng)組件就這樣搭建完成了,下面,我們將引入組件看一下效果。
<template>
<div id="app">
<fullScreenContainer>
<img alt="Vue logo" src="./assets/logo.png" />
<HelloWorld msg="Welcome to Your Vue.js App" />
</fullScreenContainer>
</div>
</template>
<script>
import HelloWorld from "./components/HelloWorld.vue";
import fullScreenContainer from "./components/fullScreenContainer/fullScreenContainer.vue";
export default {
name: "App",
components: {
HelloWorld,
fullScreenContainer,
},
};
</script>
<style>
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
}
</style>
效果很好,這樣對(duì)于一些開(kāi)發(fā)自適應(yīng)頁(yè)面非常容易。
作者:Vam的金豆之路
主要領(lǐng)域:前端開(kāi)發(fā)
我的微信:maomin9761
微信公眾號(hào):前端歷劫之路