html篇
常用的meta属性
meta 对于移动端的一些特殊属性,可以根据需要自行设置
// 页面的布局视口(layout viewport)的宽度等于视觉视口(virsual viewport)的宽度,此时页面元素会以设备逻辑像素宽度做为文档宽度进行布局
<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, width=device-width">
<meta name="screen-orientation" content="portrait"> //Android 禁止屏幕旋转
<meta name="full-screen" content="yes"> //全屏显示
<meta name="browsermode" content="application"> //UC应用模式,使用了application这种应用模式后,页面讲默认全屏,禁止长按菜单,禁止收拾,标准排版,以及强制图片显示。
<meta name="x5-orientation" content="portrait"> //QQ强制竖屏
<meta name="x5-fullscreen" content="true"> //QQ强制全屏
<meta name="x5-page-mode" content="app"> //QQ应用模式
电话号码识别(ios)
在 iOS Safari (其他浏览器和 Android 均不会)上会对那些看起来像是电话号码的数字处理为电话链接,比如:
- 7 位数字,形如:1234567
- 带括号及加号的数字,形如:(+86)123456789
- 双连接线的数字,形如:00-00-00111
- 11 位数字,形如:13800138000
关闭识别
<meta name="format-detection" content="telephone=no" />
开启识别
<a href="tel:123456">123456</a>
邮箱识别(Android)
安卓上会对符合邮箱格式的字符串进行识别,我们可以通过如下的 meta 来管别邮箱的自动识别:
<meta content="email=no" name="format-detection" />
同样地,我们也可以通过标签属性来开启长按邮箱地址弹出邮件发送的功能:
<a mailto:dooyoe@gmail.com">dooyoe@gmail.com</a>
css篇
移动端click屏幕产生200-300 ms的延迟响应
html {touch-action: manipulation;}
0.5px细线
移动端 H5 项目越来越多,设计师对于 UI 的要求也越来越高,比如 1px 的边框。在高清屏下,移动端的 1px 会很粗。下面介绍最常用的方法.
/* 四条边 */
.setBorderAll {
position: relative;
&:after {
content: ' ';
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
box-sizing: border-box;
border: 1px solid #e5e5e5;
border-radius: 4px;
}
}
屏蔽用户选择
禁止用户选择页面中的文字或者图片
div {
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
清除输入框内阴影(ios)
div {
-webkit-appearance: none;
}
输入框placeholder
字体颜色
input::-webkit-input-placeholder,
textarea::-webkit-input-placeholder {
color: #c7c7c7;
}
input:-moz-placeholder,
textarea:-moz-placeholder {
color: #c7c7c7;
}
input:-ms-input-placeholder,
textarea:-ms-input-placeholder {
color: #c7c7c7;
}
用户设置字号放大或者缩小导致页面布局错误
body {
-webkit-text-size-adjust: 100% !important;
text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
}
android系统中元素被点击时产生边框
部分android系统点击一个链接,会出现一个边框或者半透明灰色遮罩, 不同生产商定义出来额效果不一样。去除代码如下
a,button,input,textarea{
-webkit-tap-highlight-color: rgba(0,0,0,0)
-webkit-user-modify:read-write-plaintext-only;
}
js篇
audio 和 video 在 ios 和 andriod 中自动播放
由于自动播放网页中的音频或视频,会给用户带来一些困扰或者不必要的流量消耗,所以苹果系统和安卓系统通常都会禁止自动播放和使用 JS 的触发播放,必须由用户来触发才可以播放。加入自动触发播放的代码
$('html').one('touchstart', function() {
audio.play()
})
ios软键盘问题
移动端使用vue开发注意事项
分辨率rem
首先我们在项目中引入 lib-flexible:
npm install lib-flexible -S
// main.js 全局引入
...
import 'lib-flexible/flexible'
...
此时我们可以重启项目,看看 html 标签有没有 font-size 属性:
在项目根目录下新建 .postcssrc.js 配置文件,配置代码如下:
module.exports = {
plugins: {
'autoprefixer': {
browsers: ['Android >= 4.0', 'iOS >= 7'] /
},
'postcss-pxtorem': {
rootValue: 37.5, // vant-UI的官方根字体大小是37.5
propList: ['*']
}
}
}
1rem 为 37.5px,75px 等于 2rem,而 html 根节点的 font-size 属性大小会根据移动端的分辨率动态变化,所以单位的换算也会随之变化。这里要注意的是,大些的 PX,可以避开被编译成 rem 单位,用在字体和圆角上比较方便。
页面转场动画
Vue 提供了 transition
的封装组件,在切换路由的时候,可以给任何元素和组件添加进入/离开过渡。项目中具体实现如下:
// src/App.vue
<template>
<div id="app">
<div id="nav">
<router-link to="/">Home</router-link> |
<router-link to="/about">About</router-link>
</div>
<transition :name="transitionName">
<router-view class="router-view" />
</transition>
</div>
</template>
<script>
export default {
data () {
return {
transitionName: 'slide-left'
}
},
watch: {
$route(to, from) {
// 由主级到次级
// to.meta 能取到 route 路由参数中的 meta 属性
if (to.meta.index > from.meta.index) {
// 通过改变变量名称控制左右滑动
this.transitionName = 'slide-left' // 向左滑动
} else if (to.meta.index < from.meta.index) {
// 由次级到主级
this.transitionName = 'slide-right'
} else {
this.transitionName = '' //同级无过渡效果
}
}
}
}
</script>
<style lang="less">
#app {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
}
#nav {
position: absolute;
top: 0;
left: 50%;
z-index: 1000;
}
.router-view{
width: 100%;
height: auto;
position: absolute;
top: 0;
bottom: 0;
margin: 0 auto;
-webkit-overflow-scrolling: touch;
}
.slide-right-enter-active,
.slide-right-leave-active,
.slide-left-enter-active,
.slide-left-leave-active{
height: 100%;
will-change: transform;
transition: all 500ms;
position: absolute;
backface-visibility: hidden;
}
.slide-right-enter{
opacity: 0;
transform: translate3d(-100%, 0, 0);
}
.slide-right-leave-active{
opacity: 0;
transform: translate3d(100%, 0, 0);
}
.slide-left-enter{
opacity: 0;
transform: translate3d(100%, 0, 0);
}
.slide-left-leave-active{
opacity: 0;
transform: translate3d(-100%, 0, 0);
}
</style>
随后在router.js
中标记页面层级关系
// src/router/index.js
...
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: {
index: 1 // 添加 meta 属性,约定 1 为第一级
}
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue'),
meta: {
index: 2 // 添加 meta 属性,约定 2 为第二级
}
}
]
...
reset样式
@charset "utf-8";html{touch-action: manipulation;background-color:#fff;color:#000;font-size:12px;}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,#app{height: 100%;}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #313131;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
#app{height: 100%;}
使用less中mixin快捷方法
// mixin.less
@import './base.less';
@primary: #1baeae; // 主题色
@orange: #FF6B01;
@bc: #F7F7F7;
@fc:#fff;
// // 背景图片地址和大小
.bis(@url) {
background-image: url(@url);
background-repeat: no-repeat;
background-size: 100% 100%;
}
// //圆角
.borderRadius(@radius) {
-webkit-border-radius: @radius;
-moz-border-radius: @radius;
-ms-border-radius: @radius;
-o-border-radius: @radius;
border-radius: @radius;
}
// //1px底部边框
.border-1px(@color){
position: relative;
&:after{
display: block;
position: absolute;
left: 0;
bottom: 0;
width: 100%;
border-top: 1px solid @color;
content: '';
}
}
// //定位全屏
.allcover{
position:absolute;
top:0;
right:0;
}
// //定位上下左右居中
.center {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
// //定位上下居中
.ct {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
// //定位左右居中
.cl {
position: absolute;
left: 50%;
transform: translateX(-50%);
}
// //宽高
.wh(@width, @height){
width: @width;
height: @height;
}
// //字体大小,颜色
.sc(@size, @color){
font-size: @size;
color: @color;
}
.boxSizing {
-webkit-box-sizing: border-box;
-moz-box-sizing: border-box;
box-sizing: border-box;
}
// //flex 布局和 子元素 对其方式
.fj(@type: space-between){
display: flex;
justify-content: @type;
}