手机端 web 开发小记

testseasad 8年前
   <h2><strong>retina 图片兼容</strong></h2>    <p>这个 feature 应该不仅仅只关心 手机端, 而且还包括PC, 因为苹果不仅仅在手机端 (从 iphone4 开始), 而且在, MAC 上也使用了 retina. 首先, retina 的意思就是, dpr(device-pixel-ratio)是普通屏幕的两倍. 对比于 CSS 来说就是, 原来在 css 中设置的是 1px , 在实际的屏幕显示时, 也是 1px. 但是, 在 retina 下, css 的 1px 就是实际屏幕的 2px.</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/61387609681df49112c1ed2d51944439.png"></p>    <p>这样的结果就是:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/0fda7632392873f2c9a6f62b55e68332.jpg"></p>    <p>有同学会想, 会啥会这样呢? 命名看起来大小一样的, 为什么图片变模糊了?</p>    <p>这实际上是位图的特性. 比如一个 jpg 文件, 大小是 200×300. 那么他的实际屏幕像素就是 200px × 300px. 放在 dpr=1 的屏幕中, 大小设置为:</p>    <pre>  width: 200px;  height:300px;</pre>    <p>这样, 当然可以正常显示. 但在 retina 中, 每个 css px 等于 2 倍的 px. 这样, 原来正常显示的图片就被放大了一倍. 原来图片上每个点,可以正好的放在 dpr=1 的屏幕中, 而在 dpr=2 时, 每个点都被拆分了, 所以就有锯齿造成模糊的现象.</p>    <p>而解决办法也有很多.</p>    <h3><strong>使用 media query</strong></h3>    <p>这种方法适用于背景图的添加. 使用到的是 css3 提供的 device-pixel-ratio query 来进行执行. 简单的demo就是:</p>    <pre>  #myimage {      width: 400px;      height: 300px;      background: url(lo-res.jpg) 0 0 no-repeat;  }    // 这里主要针对的是 Android 的一些设备  @media  screen and (-webkit-min-device-pixel-ratio: 1.5),  screen and (min--moz-device-pixel-ratio: 1.5),  screen and (-o-min-device-pixel-ratio: 3/2),  screen and (min-device-pixel-ratio: 1.5) {      #myimage {          background-image: url(hi-res.jpg);      }  }    // 如果你想针对苹果的, 则可以直接使用:  @media only screen and (-webkit-device-pixel-ratio: 2),         only screen and (-moz-device-pixel-ratio: 2),         only screen and (-o-device-pixel-ratio: 2/1),         only screen and (device-pixel-ratio: 2) {          #myimage {          background-image: url(hi-res.jpg);      }  }</pre>    <p>但这样成本很大, 而且每次都需要准备两份, 做一些价值特别低的工作.</p>    <h3><strong>使用 js 判断</strong></h3>    <p>除了上面写冗余的 css 代码外, 还可以使用 js 进行判断. 然后,替换 data-src 里面的内容进行懒加载.</p>    <pre>  if (window.devicePixelRatio > 1) {          var images = $("img");          images.each(function(i) {              var x1 = $(this).attr('data-src');              $(this).attr('src',x1);          });      }</pre>    <p>而且,该属性的 <a href="/misc/goto?guid=4959716863055005445" rel="nofollow,noindex">支持度</a> 挺高的, 基本上所有的手机端和PC 都支持, 除了 IE8.</p>    <h3><strong>使用矢量图</strong></h3>    <p>在 web 中使用矢量图的方式有很多. 比如, SVG, Fonts. 这两个, 应该是最适合的, 不过, 在画图时, 大多都是位图的形式, 所以, 需要转换为 SVG 和 fonts 来说, 难度有点大. 针对一些小的 logo 和 icon 来说, 还是没太大的问题的. 并且, 上述的两种方式所占用的空间大小也是很小的.</p>    <h2><strong>手机基本情况</strong></h2>    <p>现在手机的问题不在 js 脚本, 而在页面渲染. 因为, 手机的屏幕显示全是通过 CPU 进行处理. 而没有像 PC 端一样有独立的显卡专门来对图像进行绘制.</p>    <h2><strong>手机上的键盘</strong></h2>    <p>一般遇到需要输入的元素标签, 比如, input 当获取焦点时, 都会触发键盘的弹出. 但, 对于 ios 和安卓, 这两者的键盘弹出的处理方式不一样.</p>    <h3><strong>ios 的键盘</strong></h3>    <p>键盘的渲染有两种方式:</p>    <ul>     <li> <p>如果 input 已经在键盘的上方, 则只是会将控件向上推一点, 推导键盘的上方.</p> </li>     <li> <p>如果 input 在键盘的下方,键盘会覆盖该控件, 并将整个页面向上推, 直到控件推到键盘上方为止.</p> </li>    </ul>    <p>另外, 当在 ios7 一下时, 如果有元素是 fixed 属性. 那么,此时打开 键盘时, fixed 有可能会当做 absolute 进行渲染. 所以, 这真是个问题.</p>    <h3><strong>android 的键盘</strong></h3>    <p>同样,也有两种情况.</p>    <ul>     <li> <p>第一种没问题</p> </li>     <li> <p>第二种, 当 input 在键盘的下方, 会将整个 document 的高度增加, 直到控件高度超过键盘高度为止.</p> </li>    </ul>    <p>对于 android 将整个 document 向上推的情况, 对于绝对定位和 fixed 属性定位来说. 会存在一定的问题. 增加 document 并未增高 viewport 的位置, 所以, 如果使用 fixed 可能会出现, 元素跑到键盘下面. 但, 由于键盘是在整个浏览器上方的, 所以, 你也不可能覆盖掉键盘. 一般的解决办法就是, 监听输入的 focus 事件, 来动态 设置fixed 的位置.(不过好复杂).</p>    <h3><strong>软盘类型</strong></h3>    <p>针对于不同的输入,键盘上显示的类型实际上是不一样的, 一般兼容性比较好的是:数字/手机号. 可以设置为:</p>    <pre>  input[type=tel]  input[type=number]</pre>    <h3><strong>软盘人工弹起</strong></h3>    <p>当用户没有触发 input 的 focus 事件. 而是开发者人工触发的, 这里就有两种不同的情况.</p>    <p><strong>IOS</strong></p>    <p>ios6 以前, 当控件触发了 focus 事件, 但, focus 不是用户触发的, 那么键盘是不会弹起的.</p>    <p>在 ios6 以后, 可以手动添加一个 autofocus 属性即可.</p>    <p><strong>Android</strong></p>    <p>只要不是用户触发的,都不能弹起.</p>    <p><strong>键盘的收起</strong></p>    <p>键盘的收起直接触发 js 的 blur 事件即可.</p>    <h2><strong>页面滚动</strong></h2>    <p>设计到页面滚动有两个事件, 一个是 scroll, 一个是 touchmove.</p>    <p>手机端为了解决性能问题, 当页面进行滚动时, js 进行的动态渲染是无效的, 即, 使用 js 改变页面上元素的位置,是无效的. 知道页面滚动结束才行. 这种效果主要体现在 scroll 事件触发的机制上. 在 ios8 以下, 当页面滚动时, js 的渲染被暂停了. 而,对于 Andriod 4.0 以上来说, scroll 触发都是连续滚动的.</p>    <p>如果你想设置局部滚动, 可以添加 -webkit-overflow-scrolling: touch css 属性.</p>    <h2><strong>flex 问题</strong></h2>    <p>由于历史原因, 想在 web 上实现 flex 的效果. 则需要注意他的兼容性, 因为 flex 有三个版本, 而且三个版本的支持性都不一样.</p>    <p>分别是:</p>    <ul>     <li> <p>display: box</p> </li>     <li> <p>display: flexbox</p> </li>     <li> <p>display: flex</p> </li>    </ul>    <p>由于 Android 使用的是 Webkit 开源内核, 我们需要给 flex 加上 webkit 前缀, 来兼容 低版本 Android.</p>    <pre>  display: -webkit-box;    display: -moz-box;    display: -ms-flexbox;    display: -webkit-flex;    display: flex;</pre>    <p>目前的兼容性是:</p>    <p><img src="https://simg.open-open.com/show/5840316b6fd5f20d7bbd2e57c10a5cf8.png"></p>    <ul>     <li> <p>old 使用的是 display: box;</p> </li>     <li> <p>tweener 使用的是 display: flexbox;</p> </li>     <li> <p>new 使用的是 display: flex;</p> </li>    </ul>    <h2><strong>fixed 问题</strong></h2>    <p>在 mobile 里面使用 fixed 是一个比较复杂的问题. 因为,在输入时, 往往还会设计到键盘的弹出, 鉴于 ios 和 android 的不同效果. fixed 有可能在键盘弹出时, 出现错位的现象.</p>    <p>例如:</p>    <p style="text-align: center;"><img src="https://simg.open-open.com/show/0da9c87784d1ae3ef397053556eabb40.png"></p>    <p>具体参考: <a href="/misc/goto?guid=4959716863153798165" rel="nofollow,noindex">fixed</a> . 在 ios5 以前是不支持 fixed, android 在 4.x 之后, fixed 基本上才可用.</p>    <h2><strong>touch 事件解析</strong></h2>    <h3><strong>300ms click 时延</strong></h3>    <p>click 时延算是, web 手机开发的一个坑吧. 因为厂商在设计手机端时, 主要, 考虑到缩放的事.</p>    <p>即:</p>    <p>当第一次点击时, 并不会触发 click 时间, 浏览器会等待 300ms 时延, 并判断你在 300ms 内,有没有再次点击, 如果有, 则会触发 zoom in 的效果。 后来, 300ms 时延被取消了, 同时, 长按选中文本也取消了. 后来觉得不行, 又加上了. 在 Chrome 32+, IE/FF 都是 Ok的. 最近在 IOS 9.3 中也得到了修复.</p>    <p>不过前期是, 你需要在 head 标签里面加上.</p>    <pre>  // 告诉浏览器取消双击放大的效果  <meta name="viewport" content="width=device-width"></pre>    <p>或者给特定的 tag 加上特定的 css 属性</p>    <pre>  html {    touch-action: manipulation;  }</pre>    <p>不过 FF 不支持.</p>    <h3><strong>click 点透</strong></h3>    <p>这个 feature 应该算是比较经典了. 当一个罩层覆盖在一个 a 标签上。</p>    <pre>  <div id="model"></div>  <a href="www.villainhr.com">www.villainhr.com</a></pre>    <p>此时, model 是覆盖在 a 标签上的. 当点击罩层, 罩层消失. (罩层绑定的是 tap 事件) 由于, click 事件有 300ms 时延, 所以, 此时 a 标签的跳转效果会触发.</p>    <p>解决办法是:</p>    <ul>     <li> <p>设置罩层延时消失效果. 延时时间设置为 300ms+ 即可.</p> </li>     <li> <p>在 touchend 里面执行 preventDefault()</p> </li>     <li> <p>使用插件禁止掉 click 事件, 转而使用模拟的.</p> </li>    </ul>    <h2><strong>触摸事件详解</strong></h2>    <p>在手机上是没有关于鼠标的相关操作的, 比如 hover, mouseover, mousenter等等. 只有, 相关的 touch 事件.</p>    <ul>     <li> <p>touchstart 当第一根手指触摸到屏幕时, 触发.</p> </li>     <li> <p>touchmove 当某一个手指在屏幕上移动时, 触发.</p> </li>     <li> <p>touchend 当手指从屏幕上移开时, 触发.</p> </li>     <li> <p>touchcancel 当手指触控被打断时触发. 具体有一下几种.</p> </li>     <li>      <ul>       <li> <p>其他事件的发生打断了 touch 事件. 比如, js 操作强制跳转</p> </li>      </ul> </li>     <li>      <ul>       <li> <p>当浏览器的 UI 覆盖到当前的 web 上</p> </li>      </ul> </li>     <li>      <ul>       <li> <p>触摸的手指数超过了浏览器的支持数量. 如果发生, 那么第一根触摸的手指会被取消</p> </li>      </ul> </li>    </ul>    <p>// 暂不支持下列两个事件</p>    <ul>     <li> <p>touchenter 当手指进入指定元素</p> </li>     <li> <p>touchleave 当手指移出指定元素</p> </li>    </ul>    <p>因为在触发 touch 事件时, 不仅仅只是相关的 touch 事件会触发, 还会触发相关的 mouse 和 click 事件. 所以, 如果 mouse 和 click 事件有影响时, 则需要显示的使用 event.preventDefault() 取消掉后面的触发机制.</p>    <p>在每个 touch 事件里面, 还会返回挂载到 event 上的属性, 常用的有:</p>    <ul>     <li> <p>touches: 当前屏幕上的属性</p> </li>     <li> <p>targetTouches: 在指定 DOM 元素上的属性</p> </li>     <li> <p>changedTouches: 返回触发时间的相关手指数. 比如, touchmove 事件中, 返回正在移动的手指. 在 touchend 事件中, 返回移除的手指.</p> </li>    </ul>    <p>并且每一个 touch 上面都会附带相关的属性.</p>    <ul>     <li> <p>identifier: 每一个手指独一无二的 ID</p> </li>     <li> <p>target: 返回一开始触发 touch 事件的 DOM 元素</p> </li>     <li> <p>screenX/Y: 返回相对于整个手机屏幕而言的位置.</p> </li>     <li> <p>pageX/Y: 返回相对于整个页面的位置, 包括 scroll 的距离.</p> </li>     <li> <p>clientX/Y: 返回相对于浏览器 viewport 的位置. 不包含 scroll 的距离.</p> </li>     <li> <p>radiusX/Y: 返回手势椭圆的长轴和短轴的大小. 目前来说, 还不支持. 和 screenX/Y 有点类似.</p> </li>    </ul>    <h3><strong>touchmove 的坑</strong></h3>    <p>移动而不滚动</p>    <p>当触发 touchmove 时. 如果,不加限制的话, 往往会触发 scroll 的效果. 为了消除这样的问题, 只需要将默认行为禁掉就 ok.</p>    <pre>  document.body.addEventListener('touchmove', function(event) {    event.preventDefault();  }, false);</pre>    <p>不要将 touchmove 来用作渲染触发</p>    <p>因为, touchmove 的机制是浏览器自身来决定的. 他触发的次数是很有限的. 所以, 我们一般只是利用 touchmove 来获得数据, 而渲染则需要使用 requestAnimationFrame .</p>    <pre>  var touches = []  canvas.addEventListener('touchmove', function(event) {    touches = event.touches;  }, false);    // Setup a 60fps timer  timer = setInterval(function() {    renderTouches(touches);  }, 15);</pre>    <h2><strong>媒体查询</strong></h2>    <p>媒体查询机制在 css2 里面就已经提出来. 比如, 针对打印机的:</p>    <pre>  <link rel="stylesheet" media="print" href="printer.css"></pre>    <p>不过, 在 css3 中, 媒体查询的机制得到了补充. 并且除了 IE8 以外, 其他浏览器或者说, 手机端都是支持的. 他主要的用途还是区分 手机端,PC端, 屏幕的大小等.</p>    <h3><strong>屏幕大小</strong></h3>    <p>先看个 demo:</p>    <pre>  <link rel="stylesheet"    media="only screen and (min-width: 641px) and (max-width: 800px)" href="ipad.css"></pre>    <p>设置当屏幕的大小为 [641,800]之间时, 才加载 ipad.css . 如果, 不符合, 浏览器会默认忽略这个 tag. 其中 only 这个 flag 是非常重要的. 他主要作用就是告诉浏览器不符合则忽略的规则.</p>    <p>通过, 屏幕的大小来判断 mobile , pad , PC 是比较有用的.</p>    <h2><strong>meta 标签</strong></h2>    <p>meta 主要用来设置网页的源信息, 比如, 缩放, 宽度等等. 最常用的就是手机端上的</p>    <pre>  <meta content="  width=device-width;  // 网页初始宽度和设备宽度一致  initial-scale=1.0;  // 初始放大效果  maximum-scale=1.0;  // 最大放大效果  user-scalable=no"   // 防止用户缩放  name="viewport" />     <meta content="yes" name="apple-mobile-web-app-capable" />  //允许全屏模式浏览  <meta content="telephone=no" name="format-detection" /> //忽略将屏幕中的数字识别为电话号码;</pre>    <p> </p>    <p> </p>    <p>来自:https://segmentfault.com/a/1190000006975034</p>    <p> </p>