一、前言页面内容超出屏幕是常态scroll-view 就是小程序提供的可滚动容器给它一个固定的高度或宽度加上 scroll-y 或 scroll-x容器内部就能独立于页面滚动。它比页面级滚动更可控——可以指定初始位置、监听滚动到顶/到底、监听滚动过程本身下拉刷新和上拉加载几乎都建立在它之上。本案例在一个页面里并排放两个滚动视图上面是纵向滚动三个整屏色块初始定位到第二页下面是横向滚动三屏色块初始偏移 50px滚动过程中在控制台打印信息。案例还带出了几个容易被忽略的配套知识skyline 渲染引擎不支持\n换行所以要切回 webview横向滚动的三个 CSS 前提以及用伪元素在色块正中叠加文字的经典定位写法。二、核心概念scroll-view 组件可滚动视图区域。scroll-ytrue允许纵向滚动scroll-xtrue允许横向滚动二者都需要容器有确定的尺寸否则滚不起来。scroll-into-view值为某子元素的 id组件初始化或该值变化时自动滚动到该元素位置。案例设为 page2所以纵向视图一打开就停在第二页。id 不能以数字开头。scroll-left横向滚动条的初始位置单位 px。案例设为 50初始显示第一页的一部分和第二页的一小部分。对应纵向的是 scroll-top。bindscrolltoupper / bindscrolltolower滚动到顶部/左侧、底部/右侧时触发是下拉加载上一页、上拉加载下一页的事件基础。bindscroll滚动过程中持续触发事件对象里带 scrollLeft/scrollTop/scrollHeight 等频率很高回调里不宜做重活。white-space: nowrap横向滚动的 CSS 前提之一。子元素默认会自动换行容器就不需要滚了所以必须禁止换行让内容溢出。display: inline-block横向滚动子元素的排布方式。块级元素各占一行会换行行内元素又设不了宽高inline-block 两者兼得。::before 伪元素在元素内部最前面插入内容。案例给每个色块用content: 第一页加绝对定位 translate(-50%, -50%) 把文字钉在正中央。webview 渲染引擎文字里的\n换行符 skyline 引擎不支持需在 app.json 中把 renderer 改为 webview——这是本案例建项目后的第一步。三、项目文件结构scroll-view-demo/ ├── app.js 小程序逻辑注册小程序实例 ├── app.json 全局配置renderer 改为 webview ├── app.wxss 全局样式.box、.title ├── sitemap.json 微信索引配置 ├── project.config.json 项目配置appid、编译设置等 ├── components/ │ └── navigation-bar/ 自定义导航栏组件 └── pages/ └── index/ ├── index.js 页面逻辑upper/lower/scroll_y/scroll_x ├── index.json 页面配置注册自定义组件 ├── index.wxml 页面结构纵向 横向两个滚动视图 └── index.wxss 页面样式色块尺寸、伪元素文字、不换行四、代码实现1. pages/index/index.wxml【结构页面】navigation-bar titlescroll-view组件 back{{false}} colorblack background#FFF/navigation-bar scroll-view classscrollarea scroll-y typelist view classbox view classtitle滚动视图示例/view view classpage-section-title textVertical Scroll\n纵向滚动/text /view scroll-view scroll-ytrue styleheight: 300rpx; classsheet scroll-into-viewpage2 bindscrolltoupperupper bindscrolltolowerlower bindscrollscroll_y view idpage1 classscroll-view-item-y page1/view view idpage2 classscroll-view-item-y page2/view view idpage3 classscroll-view-item-y page3/view /scroll-view view classpage-section-title textHorizontal Scroll\n横向滚动/text /view scroll-view scroll-xtrue stylewidth: 100%; scroll-left50 classscroll-view-x sheet bindscrollscroll_x view idpage1 classscroll-view-item-x page1/view view idpage2 classscroll-view-item-x page2/view view idpage3 classscroll-view-item-x page3/view view idpage4 classscroll-view-item-x page4/view /scroll-view /view /scroll-view2. pages/index/index.js【页面数据】// index.js Page({ /** * 事件处理函数 */ upper(e) { console.log(to upper); }, /** * 事件处理函数 */ lower(e) { console.log(to lower); }, /** * 事件处理函数 */ scroll_y(e) { console.log(vertical scrolling); }, /** * 事件处理函数 */ scroll_x(e) { console.log(Horizontal Scrolling); } })3. pages/index/index.wxss【页面样式】/**index.wxss**/ page { height: 100vh; /* flex布局 */ display: flex; /* 按纵向排列 */ flex-direction: column; } .scrollarea { flex: 1; overflow-y: hidden; } .page-section-title { font-size: 40rpx; text-align: center; margin-top: 40rpx; margin-bottom: 20rpx; } .scroll-view-item-y { height: 300rpx; } .scroll-view-item-x { /* 创建了一个内联级块容器 */ display: inline-block; width: 100%; height: 300rpx; } .sheet { font-size: 50rpx; /* 字符间距 */ letter-spacing: 1.2em; } .page1 { /* 相对定位 */ position: relative; background-color: #1AAD19; color: #FFFFFF; } /* 伪类选择器 */ .page1::before { content: 第一页; /* 绝对定位 */ position: absolute; /* 元素从父元素的顶部向下移动50% */ top: 50%; /* 元素从父元素的最左端往右移动50% */ left: 50%; /* 元素在水平方向上向左移动自身宽度的50%向上移动自身宽度的50% */ transform: translate(-50%, -50%); } .page2 { position: relative; background-color: #2782D7; color: #FFFFFF; } .page2::before { content: 第二页; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .page3 { position: relative; background-color: purple; color: #FFFFFF; } .page3::before { content: 第三页; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } /* 作业扩展横向滚动视图新增的第四项 */ .page4 { position: relative; background-color: #FF9500; color: #FFFFFF; } .page4::before { content: 第四页; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); } .scroll-view-x { /* 不换行 */ white-space: nowrap; }4. app.wxss【全局样式】/**app.wxss**/ .box { margin: 20rpx; padding: 20rpx; border: 2rpx solid silver; } .title { font-size: 40rpx; font-weight: bolder; text-align: center; margin-bottom: 30rpx; color: red; }5. app.json【全局配置】新项目默认使用 skyline 渲染引擎但它在文字中不支持\n这样的换行符所以要删掉renderer: skyline或改为 webview{ pages: [ pages/index/index ], window: { navigationBarTextStyle: black, navigationStyle: custom }, style: v2, renderer: webview, componentFramework: glass-easel, sitemapLocation: sitemap.json, lazyCodeLoading: requiredComponents }五、运行效果页面顶部是自定义导航栏「scroll-view组件」银色边框的盒子里是红色标题「滚动视图示例」。第一组标题Vertical Scroll / 纵向滚动分两行显示——这就是\n在 webview 引擎下的效果。标题下方是一个 300rpx 高的纵向滚动容器打开页面时直接停在第二页scroll-into-viewpage2 生效蓝色底白字第二页铺满视口上下各露出一点点绿色第一页和紫色第三页。用手指上下拖动可以滚回第一页、滚到第三页滚到最顶上时控制台打印to upper滚到最底下打印to lower拖动的全程持续打印vertical scrolling。第二组标题Horizontal Scroll / 横向滚动下方是横向滚动容器初始位置距最左侧 50px视口里能看到第一页的大部分和第二页的一小条横向拖动依次经过绿、蓝、紫、橙四个色块每个色块正中都钉着第一页到第四页的白字——第四项就是作业扩展新增的橙色 #FF9500写法和前三项完全一样。横向拖动全程控制台持续打印Horizontal Scrolling。六、知识点总结1、scroll-view 能滚的两个前提缺一不可容器有确定尺寸纵向styleheight: 300rpx横向width: 100%滚动方向开关scroll-y / scroll-x。没有高度的 scroll-y 容器会被内容撑开看起来根本不需要滚——这是初学者最常踩的坑。2、scroll-into-viewpage2让视图初始就定位到第二页它的值是子元素的 id且不能以数字开头2page 非法page2 合法。这个属性还有个进阶用法把它的值绑到 data 上值一变视图就滚过去是点击目录跳转到对应章节这类需求的实现基础。注意它只在值变化时滚动因此滚动后想再回到同一位置要先清空再赋值。3、本案例纵向和横向两个 scroll-view 的子元素用了重复的 id都有 page1/page2/page3。功能上没问题——scroll-into-view 只在自己容器内查找——但 id 重复不符合语义也给全局选择器埋雷。作业扩展的 page4 只加在横向视图正是为避免和纵向的 page4 撞名如果两个视图都要四页应把 id 设计成 v-page4 / h-page4。4、横向滚动靠一条 CSS 组合拳容器white-space: nowrap禁止换行子元素display: inline-block让它们既保持行内排列又能设宽高两者缺一不可——少了 nowrap子元素放不下会换行容器就不滚了少了 inline-block块级子元素各占一行同样不滚。此外子元素之间的换行和空格也会形成间隙行内元素的固有特性要求严丝合缝时应把子元素写在同一行或用 flex 布局。5、scroll-left50的初始偏移单位是px而不是 rpx纵向的 scroll-top 同理。混用单位是 scroll 类属性出错的常见原因CSS 里写 rpx、属性里写 px两套坐标不在一个体系里。6、bindscroll 触发频率非常高——滚动期间基本每帧一次。回调里只打印一行没问题但一旦开始做 setData 或复杂计算滚动就会明显掉帧。需要边滚边响应时如标题栏吸顶变色正确做法是节流throttle或者改用节流版的 bindscroll 的差值判断。7、色块正中的第一页文字用的是伪元素三件套父元素position: relative建立定位上下文伪元素position: absolute; top: 50%; left: 50%把左上角放到中心再transform: translate(-50%, -50%)往回挪自身的一半——这是水平垂直居中的经典写法比 flex 更老但兼容性更好。content属性是伪元素生效的必要条件漏写整个伪元素不渲染。8、切 webview 引擎的原因值得记住skyline 不支持文字里的\n。这个知识点反过来也成立——如果页面在 skyline 下文字不换行把\n换成两个 text 标签或改用 webview 都能解决。渲染引擎是很多文档说支持但实际没效果问题的根源。9、page { height: 100vh; display: flex; }加.scrollarea { flex: 1 }的外层骨架让最外层 scroll-view 占满视口剩余高度页面级滚动由此交给了这个容器。100vh 是视口高度flex: 1 是除去导航栏后的剩余空间两者配合才能正确撑起滚动区。10、作业扩展 page4 的做法本身没有新知识——复制一个色块改个 id 和颜色——但检查点在于新色块必须在滚动方向的末端被完整滚到。如果横向容器加了width: 100%之外的约束或子元素宽度不是 100%新增项可能挤在视口内而根本感觉不到多了东西验证扩展点要看滚动行为而不只是页面截图。七、拓展思考scroll-view 是小程序列表交互的地基商品分类页的左侧分类 右侧商品区是两个联动的 scroll-view点左侧分类改 scroll-into-view滚右侧反查当前分类聊天室是 scroll-view 加 scroll-top 钉底新消息来了自动滚到最下朋友圈式信息流是 bindscrolltolower 触发分页加载。这些场景共同的进阶话题都是性能——长列表要用虚拟渲染图片要懒加载滚动事件要节流。还有两个官方给的浓缩版值得知道onReachBottom/onPageScroll是页面级滚动的对应物不需要 scroll-view 就能实现上拉加载适合整页都是列表的简单场景而 scroll-view 自带的refresher-enabled下拉刷新属性可以把自定义 loading 状态和刷新逻辑直接挂在容器上。理解页面级滚动和容器级滚动的边界才能在每种需求面前选对工具而不是所有滚动都无脑套 scroll-view。
阅读完成 · 觉得有帮助?