β

手机项目的一点总结

小张的前端天地 396 阅读

上周自从做了一个手机QQ浏览器推广的手机页面后,然后被临时外派到一淘做一个手机推推项目。怎么说呢,虽然还被苦逼着,但是在一淘的日子也还算比较充实吧。因为这个项目还没有完全做好,我先总结一下在这一周里面我所碰到的几个问题。

首先就是最基本的,在手机端打开页面时,页面被缩放了,这个还是比较容易解决的,加一个meta就行了。不过为保险,可以加上几个以下meta(代码来自淘宝的手机端,大家可以直接参考http://m.taobao.com)。

<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" name="viewport" /> 
   <meta content="yes" name="apple-mobile-web-app-capable" /> 
   <meta content="black" name="apple-mobile-web-app-status-bar-style" /> 
   <meta content="telephone=no" name="format-detection" />

第二个,hover,在手机端支持并不是太好,安卓是支持的,但是IOS系统并不支持,所以只能用click或者touch的一些方法。

第三个,图片轮播,可能有人会笑了,确实在PC端很容易实现,各种代码也比较多,但是我想说的是,我当时实现的是click的方式,需求方说不行,要用滑动,即手机可以拖的那种。

最后在一淘无线的一个前端帮助下,顺利地实现了。他给了我一个地址:http://swipejs.com/,直接用那个JS组件就行了,为了防止被墙,我先不厚道地复制它的JS过来吧:

/*
 * Swipe 1.0
 *
 * Brad Birdsall, Prime
 * Copyright 2011, Licensed GPL & MIT
 *
*/
window.Swipe = function(element, options) {
  // return immediately if element doesn't exist
  if (!element) return null;
  var _this = this;
  // retreive options
  this.options = options || {};
  this.index = this.options.startSlide || 0;
  this.speed = this.options.speed || 300;
  this.callback = this.options.callback || function() {};
  this.delay = this.options.auto || 0;
  // reference dom elements
  this.container = element;
  this.element = this.container.children[0]; // the slide pane
  // static css
  this.container.style.overflow = 'hidden';
  this.element.style.listStyle = 'none';
  this.element.style.margin = 0;
  // trigger slider initialization
  this.setup();
  // begin auto slideshow
  this.begin();
  // add event listeners
  if (this.element.addEventListener) {
    this.element.addEventListener('touchstart', this, false);
    this.element.addEventListener('touchmove', this, false);
    this.element.addEventListener('touchend', this, false);
    this.element.addEventListener('touchcancel', this, false);
    this.element.addEventListener('webkitTransitionEnd', this, false);
    this.element.addEventListener('msTransitionEnd', this, false);
    this.element.addEventListener('oTransitionEnd', this, false);
    this.element.addEventListener('transitionend', this, false);
    window.addEventListener('resize', this, false);
  }
};
Swipe.prototype = {
  setup: function() {
    // get and measure amt of slides
    this.slides = this.element.children;
    this.length = this.slides.length;
    // return immediately if their are less than two slides
    if (this.length < 2) return null;
    // determine width of each slide
    this.width = Math.ceil(("getBoundingClientRect" in this.container) ? this.container.getBoundingClientRect().width : this.container.offsetWidth);
    // return immediately if measurement fails
    if (!this.width) return null;
    // hide slider element but keep positioning during setup
    this.container.style.visibility = 'hidden';
    // dynamic css
    this.element.style.width = Math.ceil(this.slides.length * this.width) + 'px';
    var index = this.slides.length;
    while (index--) {
      var el = this.slides[index];
      el.style.width = this.width + 'px';
      el.style.display = 'table-cell';
      el.style.verticalAlign = 'top';
    }
    // set start position and force translate to remove initial flickering
    this.slide(this.index, 0); 
    // show slider element
    this.container.style.visibility = 'visible';
  },
  slide: function(index, duration) {
    var style = this.element.style;
    // fallback to default speed
    if (duration == undefined) {
        duration = this.speed;
    }
    // set duration speed (0 represents 1-to-1 scrolling)
    style.webkitTransitionDuration = style.MozTransitionDuration = style.msTransitionDuration = style.OTransitionDuration = style.transitionDuration = duration + 'ms';
    // translate to given index position
    style.MozTransform = style.webkitTransform = 'translate3d(' + -(index * this.width) + 'px,0,0)';
    style.msTransform = style.OTransform = 'translateX(' + -(index * this.width) + 'px)';
    // set new index to allow for expression arguments
    this.index = index;
  },
  getPos: function() {
    
    // return current index position
    return this.index;
  },
  prev: function(delay) {
    // cancel next scheduled automatic transition, if any
    this.delay = delay || 0;
    clearTimeout(this.interval);
    // if not at first slide
    if (this.index) this.slide(this.index-1, this.speed);
  },
  next: function(delay) {
    // cancel next scheduled automatic transition, if any
    this.delay = delay || 0;
    clearTimeout(this.interval);
    if (this.index < this.length - 1) this.slide(this.index+1, this.speed); // if not last slide
    //else this.slide(0, this.speed); //if last slide return to start,这一行注释,是让PC端和手机端统一
  },
  begin: function() {
    var _this = this;
    this.interval = (this.delay)
      ? setTimeout(function() { 
        _this.next(_this.delay);
      }, this.delay)
      : 0;
  
  },
  
  stop: function() {
    this.delay = 0;
    clearTimeout(this.interval);
  },
  
  resume: function() {
    this.delay = this.options.auto || 0;
    this.begin();
  },
  handleEvent: function(e) {
    switch (e.type) {
      case 'touchstart': this.onTouchStart(e); break;
      case 'touchmove': this.onTouchMove(e); break;
      case 'touchcancel' :
      case 'touchend': this.onTouchEnd(e); break;
      case 'webkitTransitionEnd':
      case 'msTransitionEnd':
      case 'oTransitionEnd':
      case 'transitionend': this.transitionEnd(e); break;
      case 'resize': this.setup(); break;
    }
  },
  transitionEnd: function(e) {
    
    if (this.delay) this.begin();
    this.callback(e, this.index, this.slides[this.index]);
  },
  onTouchStart: function(e) {
    
    this.start = {
      // get touch coordinates for delta calculations in onTouchMove
      pageX: e.touches[0].pageX,
      pageY: e.touches[0].pageY,
      // set initial timestamp of touch sequence
      time: Number( new Date() )
    };
    // used for testing first onTouchMove event
    this.isScrolling = undefined;
    
    // reset deltaX
    this.deltaX = 0;
    // set transition time to 0 for 1-to-1 touch movement
    this.element.style.MozTransitionDuration = this.element.style.webkitTransitionDuration = 0;
    
    e.stopPropagation();
  },
  onTouchMove: function(e) {
    // ensure swiping with one touch and not pinching
    if(e.touches.length > 1 || e.scale && e.scale !== 1) return;
    this.deltaX = e.touches[0].pageX - this.start.pageX;
    // determine if scrolling test has run - one time test
    if ( typeof this.isScrolling == 'undefined') {
      this.isScrolling = !!( this.isScrolling || Math.abs(this.deltaX) < Math.abs(e.touches[0].pageY - this.start.pageY) );
    }
    // if user is not trying to scroll vertically
    if (!this.isScrolling) {
      // prevent native scrolling 
      e.preventDefault();
      // cancel slideshow
      clearTimeout(this.interval);
      // increase resistance if first or last slide
      this.deltaX = 
        this.deltaX / 
          ( (!this.index && this.deltaX > 0               // if first slide and sliding left
            || this.index == this.length - 1              // or if last slide and sliding right
            && this.deltaX < 0                            // and if sliding at all
          ) ?                      
          ( Math.abs(this.deltaX) / this.width + 1 )      // determine resistance level
          : 1 );                                          // no resistance if false
      
      // translate immediately 1-to-1
      this.element.style.MozTransform = this.element.style.webkitTransform = 'translate3d(' + (this.deltaX - this.index * this.width) + 'px,0,0)';
      
      e.stopPropagation();
    }
  },
  onTouchEnd: function(e) {
    // determine if slide attempt triggers next/prev slide
    var isValidSlide = 
          Number(new Date()) - this.start.time < 250      // if slide duration is less than 250ms
          && Math.abs(this.deltaX) > 20                   // and if slide amt is greater than 20px
          || Math.abs(this.deltaX) > this.width/2,        // or if slide amt is greater than half the width
    // determine if slide attempt is past start and end
        isPastBounds = 
          !this.index && this.deltaX > 0                          // if first slide and slide amt is greater than 0
          || this.index == this.length - 1 && this.deltaX < 0;    // or if last slide and slide amt is less than 0
    // if not scrolling vertically
    if (!this.isScrolling) {
      // call slide function with slide end value based on isValidSlide and isPastBounds tests
      this.slide( this.index + ( isValidSlide && !isPastBounds ? (this.deltaX < 0 ? 1 : -1) : 0 ), this.speed );
    }
    
    e.stopPropagation();
  }
};

它这个代码是用touch的相关事件来搞定手机端的写法的。在调用的时候,只需要传入外层容器的元素,当然结构要和它保持一样,即:

<div id='slider'>
  <ul>
    <li style='display:block'></li>
    <li style='display:none'></li>
    <li style='display:none'></li>
  </ul>
</div>
var slider = new Swipe(document.getElementById('slider'), {
      callback: function(e, pos) {
          //pos即为当前索引
    })

在手机端再次碰到中英文混排的问题,解决的方式用一行CSS即可:

.wrap{word-wrap:break-word;word-break:break-all;}

老实讲,一直缺少这方面给P段落加这个的意识,有时候在想到底有没有必要给全局的P加一个这个玩意。但文字也不一定出现在P标签内。

点击select,在手机端会出现一个弹层,供自己来选择相应的选项,还是挺舒服的。不过话说一开始和设计师沟通的时候,真的很纠结,因为她一直说,点击那个文字弹出可以选择的菜单。我一直表示莫名其妙,后来才真正懂了。它是把select的透明度opacity设置设为0,然后select和文字重叠即可。写了一个简单的demo:

Tips:You can change the code before run.

用非IE打开啊,因为我没对opacity做兼容处理,说真的,手机端做爽了,真心不想管IE这些鸟东西了。在PC端,点击文字,会有下拉框出来,在手机端,点击文字,直接是一个弹层,让你选择选项。

这两天也顺便接触了一个jQuery的瀑布式布局插件:Masony,因为在搜索页要用到,这个手机端的JS框架是使用jquery的。

其他记录中。。。。

不过老实说,写手机端的CSS比写PC端还舒服,至少代码少写了很多,而且可以大量使用CSS3的一些特性。不过这个项目最考察的人的地方还是在于模块化、通用性。自己也在实践的过程中,不知不觉,又提升了一些。

作者:小张的前端天地
原文地址:手机项目的一点总结, 感谢原作者分享。

发表评论