cover_image

开发 | 餐饮小程序必备!教你轻松做出像「饿了么」一样的点餐界面

cloud微信小程序小程序开发开发技巧前端开发sticky
知晓程序

<p><img class="aligncenter size-full wp-image-872597" src="http://images.ifanr.cn/wp-content/uploads/2017/07/minapp-dev-final-feature.jpg" alt="" width="1200" height="750" srcset="http://www.ifanr.com/wp-content/uploads/2017/07/minapp-dev-final-feature.jpg 1200w, http://www.ifanr.com/wp-content/uploads/2017/07/minapp-dev-final-feature-360x225.jpg 360w, http://www.ifanr.com/wp-content/uploads/2017/07/minapp-dev-final-feature-768x480.jpg 768w, http://www.ifanr.com/wp-content/uploads/2017/07/minapp-dev-final-feature-1024x640.jpg 1024w" sizes="(max-width: 1200px) 100vw, 1200px" /></p> <p><span style="color: #808080;">文 | zyh2668</span></p> <p><span style="color: #605eac;"><strong>知晓程序注:</strong></span></p> <p>许多购物、外卖小程序,都会做「分栏」设计,即在左侧展示商品分类、右侧展示分类下的具体商品。</p> <p>如何将分类栏固定在屏幕上呢?使用 sitcky 特性,或许是个方案。</p> <p>今天,知晓程序就来为大家讲解,如何在小程序中使用 sticky 的方法,将页面元素固定在屏幕上。</p> <p><span style="color: #808080;">关注「<span style="color: #605eac;">知晓程序</span>」微信公众号,回复「<span style="color: #605eac;">开发</span>」,获取小程序优化秘籍。</span></p> <hr /> <h3><span style="color: #605eac;">什么是 sticky 效果?</span></h3> <p>简单地说,<strong>sticky 就是标题栏的「粘粘」效果</strong>,向下滑动时跟着列表走、向上滑动到顶部时将会固定在顶部。</p> <p style="text-align: center;"><img class="aligncenter size-full wp-image-947212" src="http://images.ifanr.cn/wp-content/uploads/2017/11/aaa-6.jpg" alt="" width="1000" height="231" srcset="http://www.ifanr.com/wp-content/uploads/2017/11/aaa-6.jpg 1000w, http://www.ifanr.com/wp-content/uploads/2017/11/aaa-6-360x83.jpg 360w, http://www.ifanr.com/wp-content/uploads/2017/11/aaa-6-768x177.jpg 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></p> <p style="text-align: center;"><span style="color: #808080;">顶部的蓝色条幅,就是 sticky 后的效果</span></p> <p>如果不考虑不同浏览器兼容性,CSS 3 就有一个 <code>position: sticky</code> 属性,就能实现这种效果。</p> <pre><code>{ position: sticky; top: 0; }</code></pre> <p>只需要这两行就能实现,然而…… 在不同浏览器中,这个属性的兼容性,那是相当的差。</p> <h3><span style="color: #605eac;">在小程序里,如何实现固定效果?</span></h3> <p>在小程序里实现 sticky 效果,<strong>我们需要利用小程序 <code>scroll-view</code> 组件里的 <code>scroll-into-view</code> 属性</strong>。</p> <p>首先,我们需要获取每个 <code>scroll-into-view</code> 的 <code>scrollTop</code>,并且监听 <code>scroll</code> 的滚动,并改变 <code>scroll-into-view</code> 的值。</p> <p>下面,来让我们看一下具体该如何实现。</p> <pre><code>&lt;scroll-view scroll-y class="left-wrapper" id="left"&gt; &lt;view wx:for="..." bindtap="..."&gt;&lt;/view&gt; &lt;!--这里是左侧的类型选择--&gt; &lt;/scroll-view&gt; &lt;scroll-view scroll-y class="right-wrapper" bindscroll="onScroll" scroll-into-view="{{toView}}" id="right"&gt; &lt;view wx:for="{{items}}" wx-for-item="item" class="lists" id="{{item.title}}"&gt; &lt;view class="type-title" style="{{style}}"&gt; &lt;!-- 这个就是ticky header部分 --&gt; {{item.title}} &lt;/view&gt; &lt;view class="content"&gt; &lt;view wx:for="{{item.child}}" class="item"&gt; &lt;!--这里是需要展示具体的列表项--&gt; &lt;/view&gt; &lt;/view&gt; &lt;/view&gt; &lt;/scroll-view&gt;</code></pre> <p>左侧列表页没什么好讲的,无非就是按下某个类型,给上一个 <code>checked</code> 样式,然后改变 <code>toView</code> 的值。</p> <p>那么 <code>toView</code> 是什么呢?<strong>首先,<code>toView</code> 的值是和 <code>scroll-view</code> 里面你需要跳转的 <code>view</code> 的 <code>id</code> 对应起来的</strong>,也就是以下代码中的 <code>id</code>。</p> <pre><code>&lt;view wx:for="{{items}}" wx-for-item="item" class="lists" id="{{item.title}}"&gt;</code></pre> <p>当用户按下左侧对应的按钮,右侧的 <code>scroll</code> 就会跳转到相应 <code>id</code> 的 <code>scroll-into-view</code> 里面。</p> <p>到目前为止,我们已经实现了sticky header + 跳转的问题了。<strong>但如果滑动右侧的滚动条的话,左侧的数据如何跟着变化呢?</strong></p> <p>假如不是小程序的话,应该很多人都知道怎么做——无非就是监听滚动条,判断滚动条的位置,然后根据区域去改变左侧的选择。但是,<strong>小程序如何获得 <code>scroll-into-view</code> 在 <code>scroll-view</code> 里面的位置呢?</strong></p> <p>众所周知,小程序是没有类似 <code>document.getElementById()</code> 这种 DOM 操作的,也没法使用 jQuery 的 <code>$</code> 对象,快捷获取 <code>scrollTop</code>,还不能像 vue 一样,直接操作 <code>$el</code>。</p> <p>还好,小程序基础库 1.4.x 开放了一个接口:<code>wx.createSelectorQuery()</code>。</p> <p>使用这个接口,小程序将会返回一个 <code>SelectorQuery</code> 对象实例。<strong>可以在这个实例上使用 <code>select</code> 等方法选择节点,并使用 <code>boundingClientRect</code> 等方法选择需要查询的信息。</strong></p> <pre><code>nodesRef.boundingClientRect([callback])</code></pre> <p>添加节点的布局位置的查询请求,相对于显示区域,以像素为单位。其功能类似于 DOM 的 <code>getBoundingClientRect</code>。返回值是 <code>nodesRef</code> 对应的 <code>selectorQuery</code>。</p> <p>返回的节点信息中,每个节点的位置用 <code>left</code>、<code>right</code>、<code>top</code>、<code>bottom</code>、<code>width</code>、<code>height</code> 字段描述。如果提供了 <code>callback</code> 回调函数,在执行 <code>selectQuery</code> 的 <code>exec</code> 方法后,节点信息会在 <code>callback</code> 中返回。</p> <p>然后,<strong>我们可以通过这个方法拿到所有的 <code>scroll-into-view</code> 的位置。</strong></p> <pre><code>let query = wepy.createSelectorQuery() for (let i = 0; i &lt; this.types.length; ++i) { let id = this.types[i] query.select(`#${id}`).boundingClientRect((rect) =&gt; { this.scrollTops[id] = rect.top }).exec() }</code></pre> <p>需要注意的是,这个操作必须得放在 <code>onReady()</code> 的时候去做,否则将无法得到 <code>rect</code> 属性。</p> <p>得到这个属性以后其实就很好操作了,直接上代码:</p> <pre><code>onScroll (event) { // 如果是右侧的滚动view if (event.currentTarget.id === 'right') { // 判断滚动的方向 let top = event.detail.scrollTop this.dir = this.currentTop &lt; top ? 'down' : 'up' this.currentTop = top // 判断当前滚动条所在区域,如果不在当前区域则进行跳转 if (top &gt; this.scrollTops[this.getNextView()] &amp;&amp; this.dir === 'down' &amp;&amp; this.checked &lt; this.types.length - 1) { this.setChecked(this.checked + 1) } if (top &lt; this.scrollTops[this.toView] &amp;&amp; this.dir === 'up' &amp;&amp; this.checked &gt; 0) { this.setChecked(this.checked - 1) } } }</code></pre> <p>一个简单的、具有 sticky 效果的商品列表页,以及分类跳转功能,就实现了。</p> <h3><span style="color: #605eac;">坑与问题</span></h3> <p>首先,<strong><code>scroll-view</code> 必须设置高度</strong>,否则在 iOS 上,将无法使用 <code>scroll-into-view</code> 跳转。另外,<strong>页面渲染完成后,才能使用 <code>createSelectorQuery</code></strong>。</p> <p>此外,<code>scroll</code> 会有个惯性运动。这时候,按左侧的按钮切换 <code>scroll-into-view</code> 会和 <code>onScroll</code> 事件发生一些冲突,实测在 iOS 存在有该问题,希望大神给予些指导意见。</p> <h3><span style="color: #605eac;">最后的话</span></h3> <p>由于采用了 wepy 构建的小程序,所以在部分代码上会有出入或相似的地方。但我们主要学习的是思路。</p> <p>wepy 的本意是希望小程序能像 vue 一样开发,由于本人一直在用vue做项目,所以用 wepy 开发小程序会顺手一些。</p> <p>但是 wepy 虽然尽力贴合 vue,但在某些设计上存在着一定的问题。不过,使用 wepy 已经比直接开发小程序用起来舒服一些。</p> <p><strong>关注「<span style="color: #605eac;">知晓程序</span>」公众号,在微信后台回复「<span style="color: #605eac;">开发</span>」,让你的小程序性能再上一层楼。</strong></p> <p><strong><img class="aligncenter size-full wp-image-932929" src="http://ifanr-cdn.b0.upaiyun.com/wp-content/uploads/2017/11/20171101173458.jpg" alt="" width="1000" height="200" srcset="http://www.ifanr.com/wp-content/uploads/2017/11/20171101173458.jpg 1000w, http://www.ifanr.com/wp-content/uploads/2017/11/20171101173458-360x72.jpg 360w, http://www.ifanr.com/wp-content/uploads/2017/11/20171101173458-768x154.jpg 768w" sizes="(max-width: 1000px) 100vw, 1000px" /></strong></p> <p><img class="size-full wp-image-911397 aligncenter" src="http://ifanr-cdn.b0.upaiyun.com/wp-content/uploads/2017/09/bottomqrcode.gif" alt="" width="800" height="400" srcset="http://www.ifanr.com/wp-content/uploads/2017/09/bottomqrcode.gif 800w, http://www.ifanr.com/wp-content/uploads/2017/09/bottomqrcode-360x180.gif 360w, http://www.ifanr.com/wp-content/uploads/2017/09/bottomqrcode-768x384.gif 768w" sizes="(max-width: 800px) 100vw, 800px" /></p>