摘要:中中我的草稿這樣,定義了為的頁面就會被緩存。但是移動端開發不能用樹,通常就是像百度網盤那樣,類型文件夾的方式交互。
項目背景
手上的 vue移動端 項目已經開發了大幾個月了,遇到了一些很有意思的坑,也讓自己學習了很多;寫此文主要目的是記下一些我遇到的坑,以及自己的解決方案,分享的同時也方便以后復習。
項目的底層是上司通過 Cordova 等常用的 hybird app工具打包出來的。然后通過 webview 打開我的vue項目。所以嚴格意義上說,我還是在做單頁面應用。 hybird app 的底層會提供一些api 給我調用,方便我關閉打開webview,或者跳轉到不同子頁面。hybird app會集成不同的業務。這些業務有hybird app本事的服務,也有像我這種,完全來自其服務的頁面。這些就是項目大概的背景。
提示:由于是項目總結文章,可能總結點會比較混亂,部分先后,想到什么寫什么。移動端resize.css
body, h1, h2, h3, h4, h5, h6, hr, p, blockquote, dl, dt, dd, ul, ol, li, pre, form, fieldset, legend, button, input, textarea, th, td { margin:0; padding:0;box-sizing: border-box; } body, button, input, select, textarea { font:12px/1.5tahoma, arial, 5b8b4f53; } address, cite, dfn, em, var { font-style:normal; } code, kbd, pre, samp { font-family:couriernew, courier, monospace; } small{ font-size:14px; } ul, ol { list-style:none; } a { text-decoration:none; color:#000;} a:hover { text-decoration:none; } sup { vertical-align:text-top; } sub{ vertical-align:text-bottom; } legend { color:#000; } fieldset, img { border:0; } button, input, select, textarea { font-size:100%; } table { border-collapse:collapse; border-spacing:0; } input{-webkit-appearance: none;} //直接再main.js 中引入就可以,common.css 也一樣 * common.css /* * @Author lizhenhua * @version 2018/5/14 * @description */ /*--------------頭中底布局樣式*/ html { line-height: initial; } body { font-size: 0.32rem; //padding-top: constant(safe-area-inset-top); //padding-top: env(safe-area-inset-top); } html, body{ position: relative; height: 100%; /*overflow-y: auto;*/ /*overflow-x: hidden;*/ /*這里不能加overflow所有屬性,在蘋果下會有上下拉蓋住頂部底部的bug */ } .page{ height: 100vh; box-sizing: border-box; //position: relative;/*relative 不能加載page上,會導致切換動畫失效*/ } .page-overflow{ height: 100%; overflow: hidden; } .mobile-top{ background: #3275dd; position: absolute; z-index: 1000; top: 0; left: 0; right: 0; padding-top: 20px; padding-top: constant(safe-area-inset-top); /* 這里需要使用 calc 動態計算 */ padding-top: env(safe-area-inset-top); padding-left: constant(safe-area-inset-left); padding-left: env(safe-area-inset-left); padding-right: constant(safe-area-inset-right); padding-right: env(safe-area-inset-right); } .mobile-content { width: 100%; overflow: hidden; background: #f1f2f6; height: 100vh; box-sizing: border-box; position: relative; padding-top:62.5px; padding-top: calc(constant(safe-area-inset-top) + 42.5px);/*1.25rem 本身就預留了信號bar高度0.4rem,這里要減去*/ padding-top: calc(env(safe-area-inset-top) + 42.5px); padding-bottom:50px; padding-bottom: calc(constant(safe-area-inset-bottom) + 50px); padding-bottom: calc(env(safe-area-inset-bottom) + 50px); padding-left: calc(constant(safe-area-inset-left)); padding-left: calc(env(safe-area-inset-left)); } .mobile-content-pb0{ padding-bottom: 0; padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); } .mobile-bottom{ height: 1rem; height: calc(constant(safe-area-inset-bottom) + 50px); height: calc(env(safe-area-inset-bottom) + 50px); /*position: fixed;*/ position:absolute; overflow: hidden; box-shadow: 0px 0 1px 1px #ccc; background: #fff; border-bottom: 1px solid #ccc; z-index: 1000; display: flex; left: 0; right: 0; bottom: 0; padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); padding-left: constant(safe-area-inset-left); padding-left: env(safe-area-inset-left); padding-right: constant(safe-area-inset-right); padding-right: env(safe-area-inset-right); } //安卓彈窗鍵盤頂起底部的bug @media screen and (max-height: 450px) { .mobile-bottom{ display: none; } } .load-more-content{ //讓拉動屏幕底部也可以刷新 load-more min-height: 77vh; } input[readonly]{ background: #eee; } input:focus { outline: none; } .v-icon{ width: 17px; height: 17px; } .icon{ width: 17px; height: 17px; } /*動畫閃屏bug*/ .mint-loadmore-content{ -webkit-transform-style: preserve-3d; -webkit-backface-visibility: hidden; transform: translate3d(0,0,0); transform-style: preserve-3d; backface-visibility: hidden; li{ -webkit-backface-visibility: hidden; backface-visibility: hidden; } } /*end 動畫閃屏bug*/ /*fix 移動端輸入板 擋住 input ,textarea 的bug*/ .input-bug{ position: absolute; top: 20%; left: 0; right: 0; z-index: 6000; } #inputBugModel{ width: 4000px; height: 4000px; top:50%; left: 50%; transform: translate(-50%,-50%); position: absolute; background-color: #000; opacity: 0.5; z-index: 5000; } .input-bug-oh{ overflow: hidden!important; -webkit-overflow-scrolling: inherit; } /*end fix移動端輸入板 擋住 input textarea 的bug*/ /*end--------------------------- 頭中底布局樣式*/ /*-------------工具類*/ .flex-ar{ display: flex; justify-content: space-around; align-items: center; } .flex-bet{ display: flex; justify-content: space-between; align-items: center; } .fl{ float: left; } .fr{ float: right; } .clear{ *zoom: 1; } .clear:before, .clear:after { display: table; line-height: 0; content: ""; } .clear:after { clear: both; } .dian{ overflow: hidden; text-overflow: ellipsis; white-space: nowrap } .dian4{ overflow: hidden; /*超出隱藏*/ text-overflow: ellipsis; /*文本溢出時顯示省略標記*/ display: -webkit-box; /*設置彈性盒模型*/ -webkit-line-clamp: 4; /*文本占的行數,如果要設置2行加...則設置為2*/ -webkit-box-orient: vertical; /*子代元素垂直顯示*/ } .dian3 { overflow: hidden; /*超出隱藏*/ text-overflow: ellipsis; /*文本溢出時顯示省略標記*/ display: -webkit-box; /*設置彈性盒模型*/ -webkit-line-clamp: 3; /*文本占的行數,如果要設置2行加...則設置為2*/ -webkit-box-orient: vertical; /*子代元素垂直顯示*/ } .wh100{ width: 100%; height: 100%; } .oh{ overflow: hidden!important; -webkit-overflow-scrolling: inherit; } .hide{ display: none; } .no-scroll{ position: fixed; width: 100%; } .pd{ padding:0.2rem; } .pd20{ padding:0.2rem; } pl20{ padding-left:0.2rem; } pr20{ padding-right:0.2rem; } .mb0{ margin-bottom: 0; } .mb20{ margin-bottom: 0.2rem; } .mt10{ margin-top: 0.1rem; } .mt20{ margin-top: 0.2rem; } .ml10{ margin-left: 0.1rem; } .tr{ text-align: right!important; } .nowrap{ white-space: nowrap; } .ab-mid{ position: absolute; top:50%; left: 50%; transform: translate(-50%,-50%); } .no-data{ text-align: center; color: #ccc; padding: .5rem; } .clearfix:after { //在類名為“clearfix”的元素內最后面加入內容; content: "."; //內容為“.”就是一個英文的句號而已。也可以不寫。 display: block; //加入的這個元素轉換為塊級元素。 clear: both; //清除左右兩邊浮動。 visibility: hidden; //可見度設為隱藏。注意它和display:none;是有區別的。仍然占據空間,只是看不到而已; height: 0; //高度為0; font-size:0; //字體大小為0; } .no-height { height: auto !important; .mint-button { border-radius: 0; } } .bg0{ background: #fff; } .bg1{ background: #f8f8f8; } .loading{ /*css3 loading icon*/ margin: 0; padding:0; display: inline-block; width: 20px; height: 20px; border: 1px solid #3275dd; border-radius: 50%; border-left: none; animation: rotates 0.8s infinite linear; } @keyframes rotates { 0% {transform: rotate(0);} 100% {transform: rotate(360deg);} } /*動畫*/ .fade-enter-active { transition: all .2s ease; } .fade-leave-active { transition: all .3s ease; } .fade-enter, .fade-leave-to /* .slide-fade-leave-active for below version 2.1.8 */ { transform: translateX(100px); opacity: 0; } /*end動畫*/ /*end-------------工具類*/ /*-------------默認設定*/ /*end-------------默認設定*/ /*---------------form 相關*/ .form-card-input{ padding:10px 0.2rem; border: none; font-size: 14px; text-align: right; &:focus{ text-align: left; } } .form-line{ width: 100%; height: 15px; background-color: #f8f8f8; } /*小紙條*/ .paper-tips { background: #f7f7f7; padding: 0.3rem 0.2rem; font-size: 15px; .tips-top { .btn { color: #2f6fdd; } } p { padding: 0.1rem 0; color: #d9534f; line-height: 0.4rem; font-size: 13px; text-align: left; } } /*end 小紙條*/ /*行中提示*/ .tips { font-size: 14px; text-align: left; padding: 5px 15px; color: #a0a0a0; background-color: #f8f8f8; b { font-weight: normal; } } /*end行中提示*/ /*通用input框 樣式*/ .icon-input-style{ color: #191919; margin-top: 0.1rem; border: 1px solid #cccccc; border-radius: 5px; overflow: hidden; height: 30px; display: flex; align-items: center; justify-content: space-between; input{ border: none; margin: 0; padding:0 0.2rem; height: 100%; width: 100%; } .iconfont{ font-size: 20px; padding-left: 0.1rem; border-left: 1px solid #a4e1fe; } } /*end通用input框 樣式*/ .no-touch.mint-button{/*禁止點擊按鈕*/ background-color: #c8c9cc; color:#fff; } /*改 radio 控件樣式*/ .mint-radiolist /deep/ { display: flex; justify-content: space-around; .mint-cell-wrapper { font-size: 14px; padding: 0; border: none!important; background-image: none!important; background: transparent!important; } .mint-cell { min-height: auto; background: transparent!important; background-image: none!important; } .mint-radio-input:checked + .mint-radio-core { background-color: #fff; } .mint-radio-input:checked + .mint-radio-core::after { background-color: #26a2ff; } } /*------------end form相關*/ /*---------------副頁面相關*/ /*圓角彈窗*/ .radius-popup{ border-radius: 10px; overflow: hidden; } .radiusPopup{ border-radius: 5px; overflow: hidden; } /*my-popup 右劃頁面樣式*/ body{ /deep/ .my-popup { width: 100%; height: 100%; .mint-button{ height: 100%; } .mobile-content{ height: 100%; box-sizing: border-box; } } } .mint-button{ .mint-button-text{ user-select: none; } } /*end my-popup*/ /*loading圈層級*/ .mint-msgbox-wrapper{ z-index: 3000!important; .mint-msgbox{ box-shadow: 0 0 10px #ccc; } } .mint-indicator-wrapper{ z-index: 4000; } .mint-indicator-mask{ //loading 蓋住頁面 z-index: 4000; } /*end loading圈層級*/ /*表格*/ .gf-table { text-align: left; .t-head { background: #f5f5f5; font-size: 14px; height: 35px; color: #8f8f8f; } .row { height: 100%; display: flex; justify-content: space-around; align-items: center; padding: 0 0.2rem; .item { text-align: left; width: 2rem; font-size: 13px; span { color: #8f8f8f; } } .item:last-child { width: 3rem; } } .t-body .row { min-height: 50px; border-bottom: 1px solid #ededed; margin-left: 0.2rem; padding: 0 0.2rem 0 0; &:last-child { border-bottom: none; } } } /*表格end*/ /*Toast 顏色*/ .mint-toast{ z-index: 2010; word-break: break-all; } .mint-toast.is-placebottom{ font-weight: bolder; &.err{ //background: rgba(245,108,108,0.8); background: #feccd5; color:#f56c6c; } &.suc{ //background: rgba(103,194,58,0.8); background: #cdf9c3; color:#67c23a; } &.warn{ //background: rgba(230,162,60,0.8); background: #fde8af; color:#e6a23c; } &.info{ //background: rgba(144,147,153,0.7); background: #eaeaeb; color: #686b71; } } /*end Toast 顏色*/ /*end---------------副頁面相關*/上中下三部分的css定位問題。
這個問題我在 文章 中已經詳細說過。
rem 的使用;我直接在 app.vue 中添加以下方法,運行后,你會在html 標簽中看到 fontsize 設置為了50px; 表示 1rem = 50px;
created() { this.resize(document, window); }, methods:{ /*設置rem參照單位。width:1rem = 50px 所以設計稿寬 375px == 375/50 = 7.5rem * 由于頁面中有些元素用了絕對定位。特別是top,bottom。由于設備不同,計算出的rem不同, * 導致定位覆蓋。所以,建議涉及高度的 統一用 px 做單位,包括padding-top,bottom等。 * 因為高度存在滾動條,不存在適配問題。主要針對寬度做適配。 * * */ resize(doc, win) { var docE1 = doc.documentElement, resizeEvt = "orientationchange" in window ? "orientationchange" : "resize", recalc = function () { var clientWidth = docE1.clientWidth; if (!clientWidth) return; //docE1.style.fontSize = clientWidth / 375 + "px"; 這里希望設置 1rem = 1px,實驗證明,這樣做 會導致 html 的 fontsize小于 12px docE1.style.fontSize = (clientWidth / (375*2)) * 100 + "px"; //乘以100的意義是,1為了不受fontsize小于12的影響,2為了計算方便; }; if (!doc.addEventListener) return; win.addEventListener(resizeEvt, recalc, false); doc.addEventListener("DOMContentLoaded", recalc, false); }, }
使用建議:
1,少量大小的定義盡量使用px,因為對自適應效果影響不大。例如某個div的padding,設置為5px 10px,影響是不大的。
2,寬度上的定義盡量使用rem 作為單位,因為移動設備對寬度敏感,可謂寸金寸土。設置了以上代碼后,可以通過設計稿尺寸/50 得到rem單位的數值。 例如 padding:10px; 可以寫成 padding: 10px 0.2rem; 或者 padding:0.2rem;
3,高度上的定義,盡量使用px;因為本項目可以滾動內容頁,所以高度是不敏感的。設置為px 的原因是,后面定位 loadermore 組件會有幫助。當然,如果你對計算很有把握,或者頁面內容不允許滾動,也可以使用 rem;
遇到一個填寫表單點保存形成草稿模式的需求。要求在url中加入參數 id;刷新本頁面,重新通過id獲取數據回填。 vue 是單頁面應用,肯定不能全局刷新。
同事的解決方案調用保存接口,獲取到id后, 通過
this.router.push(this.$route.path + "&id=" + id);//加參數本頁并不會刷新
改變url ,然后重新申請 調用接口,拿到最新的數據,回填回去。
這樣做,理論上是行得通的。當時很危險,因為用戶操作頁面,會改變很多變量。如果回填數據后,由于沒有經歷完整的created等生命周期,這些變量還是原來狀態,容易出bug;
其次,如果像本項目那樣,需要支持 hybird app 通過url+id 的方式直接去到草稿的話,代碼不好維護。所以,最理想的做法,就是真實的重新
load 一次這個子頁面。
利用vue 的provide / inject api
* app.vue 中定義keep-alive 頁面怎么刷新data() { return { isRouterAlive: true, } }, provide() { return { reload: this.reload, } }, methods: { reload() { this.isRouterAlive = false this.$nextTick(() => (this.isRouterAlive = true)) }, } * 需要刷新的子頁面 inject: ["reload"], //需要調用的地方 let path = this.$route.path+"?id="+id this.$router.replace(path); this.reload();
這個需求很常見,有個列表頁面,點擊某一條去到詳情頁面,點擊返回,列表頁面保持狀態不變,滾動條保持原來位置。如果,詳情對數據做了改變,點擊返回,列表頁面才刷新。
* app.vue 中* route.js 中 { path: "a",//我的草稿 name: "myDraft", meta:{ keepAlive:true, }, component: resolve => require(["page/myDraft"],resolve) },
這樣,定義了meta keepAlive 為true 的頁面就會被 緩存。數據不變的情況下,點擊返回, 只要把滾動條位置設置到原來離開哪里就好了。
但是問題來了,1,從首頁進入 keepAlive 頁面,每次都要刷新,二,詳情頁如果改變了數據,返回后也要刷新 頁面。
這里我主要通過 eventBus 來解決了組件通知 頁面 刷新的問題。
細節可以看 我的筆記,最好的實踐應該是最后提到大神的鏈接文章。
* topBar.vue 組件的封裝并不難,就是預留自定cancel函數,不然就調用 app.vue 中的 backHome 函數 對返回做統一處理 inject:["backHome"], cancel(){ if(this.popup){ this.$emit("cancel") }else{ this.backHome(); } }, * app.vue provide() { return { backHome:this.backHome } }, backHome(){ //返回或退出webview let isOutsidePage = this.$route.params.inside; let from = this.$route.params.from; if(isOutsidePage=="in"){ //內頁跳轉 if(from=="CC"){ //回到a中心 this.$router.replace("/controlCenter") }else if(from=="SF"){ //回到b中心 this.$router.replace("/controlCenter2") }else { //回到原來的子頁面(從a頁到b頁前,必須要先保存lastFullPath) this.$router.replace(this.$store.getters.lastFullPath) this.$store.commit("setLastFullPath","")//置空舊路徑 } }else{//關閉webView closeWebView(); } } * router.js { path: "/myDraft/:from/:inside", name: "myDraft", component: resolve => require(["page/myDraft"],resolve) }, { path: "/myDraft", redirect: "myDraft/ll/out", },
通過上面的定義 //hybrid app 只需要調用 ip:xxxx/myDraft 就能打開這個頁面,并且返回鍵自動關閉webview;
通過 CC CF 等標志字符 可以判斷來自哪個 中心的。
最后來到重點的 子頁跳子頁返回 操作,主要就是需要借助vuex 保存舊 路徑
a.vue 子頁 //跳轉前先把當前路徑保存到全局vuex變量lastFullPath this.$store.commit("setLastFullPath",route.fullPath)//保存路由用于返回本頁 this.$router.replace("/ ");//清空路由,不重置會導致url 混亂。 this.$router.replace(`b/`+route.name+`/in?id=`+id);eventBus 使用
bus.vue import Vue from "vue" export default new Vue() //監聽事件 Bus.$on("update", (param) => { //監聽數據變動 this.updatexxx(param); }) //觸發事件 Bus.$emit("update",param) //銷毀事件監聽 Bus.$off("update");用鉆層列表 代替 樹形組件
樹形選擇 組件在pc端是常常用到的。特別是一些有明確層級關系,又需要勾選的數據。
但是移動端開發不能用樹,通常就是像百度網盤那樣,類型文件夾的方式交互。
我項目是選擇部門,然后選擇人員,勾選或者取消。支持快速查詢選擇。
我的思路是,設置兩個組件,一個presonInput,一個personBox;
personInput 主要用于表單中的顯示,支持輸入中文或者拼音,查找并生成選中人員。
personBox 便于選擇多個人或部門,是一個頁面大小的彈窗頁,鉆層列表,支持搜索。
input和Box 兩個組件 都通過v-model 為父頁面 維護同一組數據。就是選擇的人員的數組。
* personInput.vue 核心代碼 created(){ document.addEventListener("touchstart",(e)=>{ //點擊其他地方下拉框消失 if(this.$refs["con"]&&!this.$refs["con"].contains(e.target)){ this.visible=false; } }) }, mounted(){ Bus.$emit("updateHasSelectPerson");//通知selectPerson 組件更新緩存; }, cancelSelect(item) { //用這一句會不準確,請用findIndex // this.hasSelectPerson.splice(this.hasSelectPerson.indexOf(item),1); this.hasSelectPerson.splice(this.hasSelectPerson.findIndex(k => k.id == item.id), 1); Bus.$emit("updateHasSelectPerson"); }, selected(item) { this.visible = false; this.inputText = ""; if (this.one) { this.hasSelectPerson.splice(0);//先清空數組 }else if(this.limit&&this.hasSelectPerson.length==this.limit){ this.sureTips("最多選擇 "+this.limit+" 個人"); return; } //從帶部門的接口中,選擇出id與 人員接口的userCode 相同的人 this.$http({ url: this.ajaxApi.department.search, type: "post", data: { key: item.name, } }).then(res=>{ let theGuy = res.filter(i=>{ return i.id == item.userCode }) this.hasSelectPerson.push(theGuy[0]); }) Bus.$emit("updateHasSelectPerson"); //通知personBox 組件同步更新數據 },
personBox 核心代碼
上一層 上一層 下一層 下一層
{{item.display}}{{item.display}}
- 暫無數據
已選擇的人員:
- {{item.name}}×
父組件使用
效果permitMen: [],
input效果
personBox 效果
比較兩個對象是否相等eq(a, b, aStack, bStack) { var toString = Object.prototype.toString; function isFunction(obj) { return toString.call(obj) === "[object Function]" } function eq(a, b, aStack, bStack) { // === 結果為 true 的區別出 +0 和 -0 if (a === b) return a !== 0 || 1 / a === 1 / b; // typeof null 的結果為 object ,這里做判斷,是為了讓有 null 的情況盡早退出函數 if (a == null || b == null) return false; // 判斷 NaN if (a !== a) return b !== b; // 判斷參數 a 類型,如果是基本類型,在這里可以直接返回 false var type = typeof a; if (type !== "function" && type !== "object" && typeof b != "object") return false; // 更復雜的對象使用 deepEq 函數進行深度比較 return deepEq(a, b, aStack, bStack); }; function deepEq(a, b, aStack, bStack) { // a 和 b 的內部屬性 [[class]] 相同時 返回 true var className = toString.call(a); if (className !== toString.call(b)) return false; switch (className) { case "[object RegExp]": case "[object String]": return "" + a === "" + b; case "[object Number]": if (+a !== +a) return +b !== +b; return +a === 0 ? 1 / +a === 1 / b : +a === +b; case "[object Date]": case "[object Boolean]": return +a === +b; } var areArrays = className === "[object Array]"; // 不是數組 if (!areArrays) { // 過濾掉兩個函數的情況 if (typeof a != "object" || typeof b != "object") return false; var aCtor = a.constructor, bCtor = b.constructor; // aCtor 和 bCtor 必須都存在并且都不是 Object 構造函數的情況下,aCtor 不等于 bCtor, 那這兩個對象就真的不相等啦 if (aCtor !== bCtor && !(isFunction(aCtor) && aCtor instanceof aCtor && isFunction(bCtor) && bCtor instanceof bCtor) && ("constructor" in a && "constructor" in b)) { return false; } } aStack = aStack || []; bStack = bStack || []; var length = aStack.length; // 檢查是否有循環引用的部分 while (length--) { if (aStack[length] === a) { return bStack[length] === b; } } aStack.push(a); bStack.push(b); // 數組判斷 if (areArrays) { length = a.length; if (length !== b.length) return false; while (length--) { if (!eq(a[length], b[length], aStack, bStack)) return false; } } // 對象判斷 else { var keys = Object.keys(a), key; length = keys.length; if (Object.keys(b).length !== length) return false; while (length--) { key = keys[length]; if (!(b.hasOwnProperty(key) && eq(a[key], b[key], aStack, bStack))) return false; } } aStack.pop(); bStack.pop(); return true; } return eq(a, b, aStack, bStack) },輸入面板 擋住 textarea 或者 input
移動端常見問題,原因上網找找。特征也比較明顯,就是視口高度改變了,某些手機會觸發 onresize 事件。
解決方案有很多,因為我的例子比較極端。自己搞出來一個比較極端的方案。就是把 整個 輸入區域 定位到頂部,輸入完后恢復。
雖然極端,個人覺得也算是一個通用做法,不用考慮滾動,兼容各種莫名其妙的問題。
/** * 作者:lzh * 功能:解決移動端輸入板擋住輸入框bug * 參數:id,需要修復點擊bug的父元素id; * 參數:pullClass,需要被提起的盒子class; * 參數:scrollContentClass,發生滾動的盒子class,默認mobile-content; * 參數:top,發生滾動的盒子class,默認mobile-content; * 說明:fixBug,只有在原生標簽 加上fixBug="true" 自定義屬性才彈起修復; * 返回值: */ fixInputBug(id="app",pullClass="form-item",scrollContentClass="mobile-content",top=100){ var mobileArr = ["iPhone", "iPad", "Android", "Windows Phone", "BB10; Touch", "BB10; Touch", "PlayBook", "Nokia"]; var ua = navigator.userAgent; var res = mobileArr.filter(function (arr) { return ua.indexOf(arr) > 0; }); var nodeObj = document.getElementById(id); if (res.length > 0) { nodeObj.onclick = function (ev) { var ev = ev || nodeObj.event; var target = ev.target || ev.srcElement; let content = findParent(target,pullClass); let father = findParent(target,scrollContentClass); let scrollTop = father.scrollTop; let model = document.createElement("div"); model.id = "inputBugModel"; if (target.nodeName.toLowerCase() == "input" || target.nodeName.toLowerCase() == "textarea") { if(target.type!=="radio"&&target.type!=="checkbox"&&target.getAttribute("fixBug")){ addClass(content,"input-bug") addClass(father,"input-bug-oh") if(document.getElementById("inputBugModel")){ father.removeChild(document.getElementById("inputBugModel")); } father.appendChild(model); father.scrollTop = top; target.onblur = function () { removeClass(content,"input-bug") removeClass(father,"input-bug-oh") father.removeChild(model); father.scrollTop = scrollTop; } } } } function addClass(node,className) { if(node.className.split(" ").indexOf(className)==-1){ node.className = node.className + " " + className; } } function removeClass(node,className) { node.className = node.className.replace(" "+className, ""); } function findParent(node, className){ let target = node; if (target && target.parentNode&&target.parentNode.nodeName!=="HTML") { if(target.parentNode.className.split(" ").indexOf(className)!==-1){ return target.parentNode; } else { return findParent(target.parentNode,className) } } else { return document.getElementsByTagName("body")[0]; } } } }, * css /*fix 移動端輸入板 擋住 input ,textarea 的bug*/ .input-bug{ position: absolute; top: 20%; left: 0; right: 0; z-index: 6000; } #inputBugModel{ width: 4000px; height: 4000px; top:50%; left: 50%; transform: translate(-50%,-50%); position: absolute; background-color: #000; opacity: 0.5; z-index: 5000; } .input-bug-oh{ overflow: hidden!important; -webkit-overflow-scrolling: inherit; } /*end fix移動端輸入板 擋住 input textarea 的bug*/使用
mounted(){ this.tools.fixInputBug("permitFlowContent"); },效果 移動端快速點擊
由于移動端瀏覽器存在300ms 延遲,某些組件需要快速響應點擊事件,例如 - 0 + 組件;
利用 fastclick 插件 封裝了一個組件
使用
輸入板頂起底部 button
focus 的時候,由于底部的 mobile-bottom 部分是 absolute 的,所以被頂起來。
網上很多說法通過js判斷 onresize 事件 控制 底部顯示隱藏。可以實現,但是存在兼容性問題。且代碼啰嗦
這里直接通過css 媒體查詢實現了。
@media screen and (max-height: 450px) { .mobile-bottom{ display: none; } }適配 iphoneX
蘋果給出了 iphone的 有效區域概念。只要給碰到邊框的大div做些css兼容寫法就可以了。
設置高,寬,top,left,right,bottom 的都加上兼容。
原來代碼
.mobile-top{ background: #3275dd; position: absolute; z-index: 1000; top: 0; left: 0; right: 0; padding-top: 20px; } .mobile-content { width: 100%; overflow: hidden; background: #f1f2f6; height: 100vh; box-sizing: border-box; position: relative; padding-top:62.5px; padding-bottom:50px; } .mobile-bottom{ height: 1rem; /*position: fixed;*/ position:absolute; overflow: hidden; box-shadow: 0px 0 1px 1px #ccc; background: #fff; border-bottom: 1px solid #ccc; z-index: 1000; display: flex; left: 0; right: 0; bottom: 0; }
兼容代碼
.mobile-top{ background: #3275dd; position: absolute; z-index: 1000; top: 0; left: 0; right: 0; padding-top: 20px; padding-top: constant(safe-area-inset-top); /* 這里需要使用 calc 動態計算 */ padding-top: env(safe-area-inset-top); padding-left: constant(safe-area-inset-left); padding-left: env(safe-area-inset-left); padding-right: constant(safe-area-inset-right); padding-right: env(safe-area-inset-right); } .mobile-content { width: 100%; overflow: hidden; background: #f1f2f6; height: 100vh; box-sizing: border-box; position: relative; padding-top:62.5px; padding-top: calc(constant(safe-area-inset-top) + 42.5px);/*1.25rem 本身就預留了信號bar高度0.4rem,這里要減去*/ padding-top: calc(env(safe-area-inset-top) + 42.5px); padding-bottom:50px; padding-bottom: calc(constant(safe-area-inset-bottom) + 50px); padding-bottom: calc(env(safe-area-inset-bottom) + 50px); padding-left: calc(constant(safe-area-inset-left)); padding-left: calc(env(safe-area-inset-left)); } .mobile-bottom{ height: 1rem; height: calc(constant(safe-area-inset-bottom) + 50px); height: calc(env(safe-area-inset-bottom) + 50px); /*position: fixed;*/ position:absolute; overflow: hidden; box-shadow: 0px 0 1px 1px #ccc; background: #fff; border-bottom: 1px solid #ccc; z-index: 1000; display: flex; left: 0; right: 0; bottom: 0; padding-bottom: constant(safe-area-inset-bottom); padding-bottom: env(safe-area-inset-bottom); padding-left: constant(safe-area-inset-left); padding-left: env(safe-area-inset-left); padding-right: constant(safe-area-inset-right); padding-right: env(safe-area-inset-right); }封裝可用的阿里icon組件
* 復制阿里圖標庫的代碼到alifont.css,并在main.js 中引入 //引入阿里圖標 import "@/assets/icon/alifont.css"使用
leftClass 是你在阿里icon上面拿到的name
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/102438.html
摘要:官網還不斷的訪問不了。在此推薦一個移動端庫按需引入二次封裝組件列表的下拉刷新和上拉加載更多是移動端必須的組件。不用寫死高度了,并且兼容對外提供了更加簡明易用的刷新,回到頂部,獲得和設置滾動條位置的方法統一的提示,免去重復代碼。 按需引入mint-ui 本項目用了 mint-ui 作為基礎ui框架,在使用中遇到不少問題。官網doc 還不斷的訪問不了。不過還是很感謝 mint-ui 團隊。...
摘要:背景個人背景就讀于東北某普通二本院校計算機軟件工程專業,現大四,北京實習前端方向,自學,技術棧時間背景大概是在月日準備好簡歷開始投遞秋招差不多已經結束招聘崗位不多,投遞對象為大一些的互聯網公司事件背景第一個入職的是好未來的前端實習崗,待遇工 背景 個人背景 就讀于東北某普通二本院校計算機軟件工程專業,現大四,北京實習 前端方向,自學,vue技術棧 時間背景 大概是在11月9日準備...
摘要:背景個人背景就讀于東北某普通二本院校計算機軟件工程專業,現大四,北京實習前端方向,自學,技術棧時間背景大概是在月日準備好簡歷開始投遞秋招差不多已經結束招聘崗位不多,投遞對象為大一些的互聯網公司事件背景第一個入職的是好未來的前端實習崗,待遇工 背景 個人背景 就讀于東北某普通二本院校計算機軟件工程專業,現大四,北京實習 前端方向,自學,vue技術棧 時間背景 大概是在11月9日準備...
閱讀 3557·2021-08-02 13:41
閱讀 2390·2019-08-30 15:56
閱讀 1519·2019-08-30 11:17
閱讀 1174·2019-08-29 15:18
閱讀 580·2019-08-29 11:10
閱讀 2671·2019-08-26 13:52
閱讀 508·2019-08-26 13:22
閱讀 2949·2019-08-23 15:41