├── .gitignore ├── QR-Code.png ├── README.md ├── app └── src │ ├── audios │ └── background.mp3 │ ├── fonts │ ├── my-custom-icon-font.eot │ ├── my-custom-icon-font.svg │ ├── my-custom-icon-font.ttf │ └── my-custom-icon-font.woff │ ├── images │ ├── favicon.png │ ├── github-1.png │ ├── github-2.png │ ├── github-3.png │ └── loading.svg │ ├── index.html │ ├── javascripts │ ├── animation-control.js │ └── main.js │ └── scss │ ├── base │ ├── _base.scss │ └── _reset.scss │ ├── components │ ├── _btn-music.scss │ ├── _icon-font.scss │ ├── _loading-overlay.scss │ ├── _swiper-container.scss │ ├── _swiper-slide.scss │ └── _up-arrow.scss │ ├── helpers │ ├── _functions.scss │ ├── _global-variables.scss │ └── _mixins.scss │ ├── main.scss │ └── slides │ ├── _slide-1.scss │ ├── _slide-2.scss │ └── _slide-3.scss ├── config └── vendors.js ├── demo.jpg ├── gulpfile.js └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | app/dist/ 3 | .idea/ -------------------------------------------------------------------------------- /QR-Code.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/QR-Code.png -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # wechat-h5-boilerplate 2 | 3 | > 「让制作H5像制作PPT一样简单!」 4 | 5 | Wechat-H5-Boilerplate(以下简称WHB)是一个H5动效模板,专门为微信优化,适合快速构建全屏滚动型H5宣传页。 6 | 7 | ![效果图][1] 8 | 9 | 例如让一段文字动起来只需要一行代码: 10 | 11 |

I'm a coder!

12 | 13 | 14 | ## 目录 15 | 16 | - [实时预览](#实时预览) 17 | - [依赖列表](#依赖列表) 18 | - [项目结构](#项目结构) 19 | - [开发流程](#开发流程) 20 | - [发布流程](#发布流程) 21 | - [开发指南](#开发指南) 22 | - [待办事项](#待办事项) 23 | 24 | ## 实时预览 25 | 26 | **使用手机**访问下面的地址或扫描下方二维码 27 | 28 | [https://panteng.github.io/wechat-h5-boilerplate/][2] 29 | 30 | ![此处输入图片的描述][3] 31 | 32 | ## 依赖列表 33 | 1. [Swiper][4] --> 用于实现页面的整屏滚动 34 | 2. [Animate.css][5] --> 用于提供CSS动画 35 | 3. [jQuery][6] --> 用于操作DOM 36 | 4. [Normalize.css][7] --> 用于CSS Reset 37 | 38 | ## 项目结构 39 | 40 | /app 41 | /dist --> 项目文件的分发版本,所有的文件均由Gulp任务生成,请勿手动修改 42 | /audios --> 从app/src/audios复制而来 43 | /fonts --> 从app/src/fonts和在config/vendors.js中指定的第三方字体复制而来 44 | /images --> 由app/src/images下的图片经Imagemin压缩优化生成 45 | /javascripts --> 由app/src/javascripts下的文件经Browserify打包生成 46 | /stylesheets --> 由app/src/scss下的文件编译生成 47 | index.html --> 由app/src/index.html经Gulp-inject插入bundle.(min.).css和bundle.(min.).js后生成 48 | /src --> 项目的源码,所有文件都可编辑 49 | /audios --> 存放音频、视频文件 50 | /fonts --> 存放字体文件 51 | /images --> 存放图片文件 52 | /javascripts --> JS源文件,经Browserify打包后生成app/dist/javascripts/bundle.js 53 | /scss --> SCSS文件,经过编译后生成app/dist/stylesheets/bundle.css 54 | index.html --> 页面HTML,经过Gulp-inject处理后生成app/dist/index.html 55 | /config 56 | vendors.js --> 第三方CSS、JS、Fonts列表,详见vendors.js说明 57 | .gitignore 58 | gulpfile.js --> Gulp任务 59 | package.json 60 | 61 | 62 | ## 开发流程 63 | 64 | 1. **将本项目clone到本地** 65 | 66 | 在控制台中运行: 67 | 68 | git clone --depth=1 https://github.com/panteng/wechat-h5-boilerplate.git 69 | cd 70 | 71 | 或者你也可以直接在[Release](https://github.com/panteng/wechat-h5-boilerplate/releases)页面下载WHB的源码压缩包。 72 | 73 | 2. **安装第三方包** 74 | 75 | WHB使用NPM管理第三方包 76 | 77 | 在控制台中运行: 78 | 79 | npm install 80 | 81 | 注意1:由于中国网络环境恶劣,下载NPM上的包速度很慢,建议使用淘宝NPM镜像[CNPM][8]。CNPM的安装方法请参考[官网使用说明][9]。CNPM v4.2.0在Windows系统上有Bug(参考[#97](https://github.com/cnpm/cnpm/issues/97)),Windows用户请勿使用该版本,虽然官方说已经修复,但我在Windows上使用CNPM安装需要node-gyp编译的包时仍然会报错。我也不建议使用CNPM v3.4.1,因为其内置的NPM版本过旧。推荐`npm install --registry=https://registry.npm.taobao.org -d`这种直接使用镜像仓库的方式安装。(加入 -d 是为了显示安装过程中的详细信息,我个人经常用这种方法来判断安装过程是否因为网络或其他问题卡住了)。 82 | 83 | 注意2:WHB所需的一些第三方包依赖于[node-gyp][10],在安装这些包之前,请先确认你的机器已经正确安装node-gyp。请参考[node-gyp官方文档][11]来进行安装。Windows用户可能会遇到一些麻烦,因为在Windows上安装node-gyp是一件很痛苦的事。 84 | 85 | 注意3:Windows用户,请不要将WHB放在路径太深的目录中。因为Windows只支持长度为255个字符以内的路径,所以如果你将本项目放在路径很深的目录中,有很大可能会造成node-gyp编译失败。 86 | 87 | 注意4:Windows用户,如果你已经正确安装了node-gyp,但在运行`npm install -d`时依然报错,且报错信息为“EPERM, operation not permitted”的话,请尝试`npm install -d --force`。 88 | 89 | 3. **开始开发** 90 | 91 | 在控制台运行: 92 | 93 | gulp dev 94 | 95 | 稍等片刻,浏览器窗口会自动打开并指向地址`localhost:3000`,当你修改app/src下的任意文件时,浏览器页面会自动刷新。 96 | 97 | ## 发布流程 98 | 99 | 1. 执行gulp prod任务 100 | 101 | 在控制台中运行: 102 | 103 | gulp prod 104 | 105 | 该任务将在app/dist文件夹中生成两个新文件bundle.min.css和bundle.min.js,并删除原有的bundle.css和bundle.js。 106 | 107 | 2. 发布时,只需要将app/dist文件夹中的文件上传到服务器即可,其他文件都不是必需的。app/dist中的CSS、JS和图片文件都是经过压缩优化的。 108 | 109 | ## 开发指南 110 | 111 | 1. **加载动画** 112 | 113 | H5页面通常包含大量图片和背景音乐,因此一个好看的加载动画是必要的。 114 | 115 | 你可以自己写一些CSS3动画,将动画相关的HTML代码插入在app/src/index.html中的`
`中,并将相关的CSS3 Animation代码整合进app/src/scss中。 116 | 117 | 想省事的话,[loading.io][12]这个网站可以帮你生成现成的加载动画(打不开请翻墙)。建议生成SVG格式的动画图像文件,将文件改为为loading.svg并替换app/src/images/下的同名文件即可。 118 | 119 | 此外分享一些优秀的CSS3加载动画库: 120 | 121 | - [Spinkit][13] 122 | - [CSS Loaders][14] 123 | 124 | 2. **页面切换效果** 125 | 126 | 页面切换暂时只支持Swiper自带的四种:slide,fade, flip和coverflow(cube不支持,因为不符合此场景)。详见[Swiper文档][15]中关于effect的部分。 127 | 128 | WHB尚无法对不同页面指定不同的切换方式,我将在后续版本中考虑加入此功能以及更多更酷的切换方式。 129 | 130 | 3. **页面内元素(图片、文字)动画** 131 | 132 | 在WHB中为图片和文字添加动画非常简单。 133 | 134 | 例如在第一页中有一段文字需要以动画显示,代码如下: 135 | 136 |
137 |

I'm a coder!

138 |
139 | 140 | 只需要在这段文字上添加类名`animated`,并指定`data-ani-name`(动画名称),`data-ani-duration`(动画执行时间),`data-ani-delay`(动画延迟时间)三个属性即可。 141 | 142 | WHB的动画由Animate.css提供,支持的动画名称可以在[Animate.css官网][16]上查看。 143 | 144 | 4. **字体图标** 145 | 146 | WHB自带的字体图标文件中只有两个图标,分别是右上角的音乐符号,和屏幕下方的上划提示符号。如果你需要更多图标,建议使用[Icomoon.io][17]进行定制,选择你需要的图标(也可以自己设计并上传),打包成字体文件即可。 147 | 148 | 这里不建议使用Font-Awesome等通用字体包的原因是,Font-Awesome中的图标非常多,所以字体文件会比较大,而其中大部分图标是用不到的。较大的字体文件会拖慢网页在用户设备上的加载速度。 149 | 150 | 5. **图像优化** 151 | 152 | WHB中自带对app/src/images下的图片进行有损压缩的功能,但我仍然建议在你将图片扔进app/src/images文件夹前,对图片进行必要的手动优化,例如将图片调整为合适的尺寸,将部分小图片整合成精灵图等。 153 | 154 | 分享一些好用的图片优化处理网站: 155 | - 图片压缩:[TinyPNG][18] 156 | - 精灵图生成器(包括生成图片和CSS代码):[CSS Sprite Generator][19] 157 | - 精灵图CSS代码生成器(自动识别精灵图中的元素并生成CSS代码):[Sprite Cow][20] 158 | 159 | 6. **背景音乐** 160 | 161 | 建议背景音乐的文件格式为mp3,且大小尽量不要超过1MB。可使用[Adobe Audition][21]等专业音频编辑软件对背景音乐进行截取和压缩。 162 | 163 | 如非必要,请不要设置背景音乐来打扰用户。 164 | 165 | 7. **移动端调试** 166 | 167 | 首先,运行`gulp dev`任务,在控制台的输出信息中找到下面这段: 168 | 169 | [BS] Access URLs: 170 | ---------------------------------------- 171 | Local: http://localhost:3000 172 | External: http://192.168.187.101:3000 173 | ---------------------------------------- 174 | UI: http://localhost:3001 175 | UI External: http://192.168.187.101:3001 176 | ---------------------------------------- 177 | 178 | 然后,确保你的移动设备(手机、平板等)和电脑处于同一局域网内(最简单的方式就是让电脑、手机和平板连接同一个WIFI;或者电脑用网线连接路由器,手机和平板连接同是这个路由器发出的WIFI)。 179 | 180 | 最后,在你的移动设备上打开浏览器,访问上面第三行中External对应的URL(注意你的URL可能跟我上面写的不一样,以你自己的控制台中显示的External URL为准)。 181 | 182 | 现在只要你修改app/src下的文件,所有访问这个URL的移动设备和电脑都会自动刷新浏览器页面。你在其中一个设备上进行的操作也会实时同步到其他设备(比如用手指向上划动页面)。 183 | 184 | 8. **响应式设计** 185 | 186 | 建议使用rem替代px来设置元素的尺寸、边距和字体大小。 187 | 188 | 在WHB中,1rem对应的px数值会随着设备屏幕尺寸的不同而变化。 189 | 190 | 在屏幕宽度小于400px的设备上,1rem = 16px; 191 | 192 | 在屏幕宽度大于400px且小于600px的设备上,1rem = 22px; 193 | 194 | 在屏幕宽度大于600px的设备上,1rem = 32px; 195 | 196 | 参见app/src/scss/base/_base.scss中关于Media Query的代码。 197 | 198 | 9. **config/vendors.js说明** 199 | 200 | vendors.js文件用来登记第三方CSS、JS和Fonts信息,凡在vendors.js中登记过的第三方文件,均会以某种形式被添加到项目中。举个例子: 201 | 假如现在vendors.js是这样的: 202 | 203 | module.exports = { 204 | stylesheets: [ 205 | 'node_modules/normalize.css/normalize.css', 206 | 'node_modules/swiper/dist/css/swiper.css', 207 | 'node_modules/animate.css/animate.css', 208 | 'node_modules/font-awesome/css/font-awesome.css' 209 | ], 210 | javascripts: [ 211 | 'node_modules/jquery/dist/jquery.js', 212 | 'node_modules/swiper/dist/js/swiper.jquery.js' 213 | ], 214 | fonts: [ 215 | 'node_modules/font-awesome/fonts/*' 216 | ] 217 | }; 218 | 219 | vendors.js的stylesheets中所有的css文件均会被加入项目最终生成的bundle.css中; 220 | 221 | vendors.js的javascripts中所有的js文件均会被加入项目最终生成的bundle.js中; 222 | 223 | vendors.js的fonts中所有的文件均会被复制到app/dist/fonts文件夹中。 224 | 225 | 记住vendors.js中登记的文件会被优先加入bundle.css和bundle.js中,因此你无需担心自己写的SCSS中的样式被覆盖或者在main.js中发现某个第三方库的对象未定义的情况。而在vendors.js中登记的文件会按照登记顺序依次加入bundle.css和bundle.js,因此你要确保登记顺序正确,比如jquery.js一定要在swiper.jquery.js前面登记,因为swiper.jquery.js是依赖于jquery.js的。 226 | 227 | 注意1:文件路径是相对于gulpfile.js的,不是相对于vendors.js的。 228 | 229 | 注意2:如果你不喜欢通过这种方式引入第三方JS文件,而想使用CommonJS的require写法来引入,也是可以的。例如在app/src/javascripts/main.js中用这种方式引入jQuery: 230 | 231 | var $ = require('jquery'); 232 | 233 | 234 | 10. **Gulp任务** 235 | 236 | 在开发过程中,如果你发现明明已经修改或替换了app/src文件下的图片、音频、字体等文件,但浏览器中的页面却没有做出相应改变的话,请尝试在控制台中重新运行`gulp dev`任务。 237 | 238 | ## 待办事项 239 | 1. 完善开发文档 240 | 2. 优化动画效果 241 | 3. 优化元素定位方式 242 | 4. 增加更多Slide切换效果 243 | 244 | ## Lisence 245 | [MIT][22] 246 | 247 | 248 | [1]: https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/master/demo.jpg 249 | [2]: https://panteng.github.io/wechat-h5-boilerplate/ 250 | [3]: https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/master/QR-Code.png 251 | [4]: https://github.com/nolimits4web/swiper/ 252 | [5]: https://github.com/daneden/animate.css 253 | [6]: https://github.com/jquery/jquery 254 | [7]: https://github.com/necolas/normalize.css 255 | [8]: http://npm.taobao.org/ 256 | [9]: http://npm.taobao.org/ 257 | [10]: https://github.com/nodejs/node-gyp 258 | [11]: https://github.com/nodejs/node-gyp#installation 259 | [12]: http://loading.io/ 260 | [13]: http://tobiasahlin.com/spinkit/ 261 | [14]: http://projects.lukehaas.me/css-loaders/ 262 | [15]: http://idangero.us/swiper/api 263 | [16]: https://daneden.github.io/animate.css/ 264 | [17]: https://icomoon.io/#icon-font 265 | [18]: https://tinypng.com/ 266 | [19]: http://spritegen.website-performance.org/ 267 | [20]: http://www.spritecow.com/ 268 | [21]: http://www.adobe.com/products/audition.html 269 | [22]: http://opensource.org/licenses/mit-license.html 270 | -------------------------------------------------------------------------------- /app/src/audios/background.mp3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/audios/background.mp3 -------------------------------------------------------------------------------- /app/src/fonts/my-custom-icon-font.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/fonts/my-custom-icon-font.eot -------------------------------------------------------------------------------- /app/src/fonts/my-custom-icon-font.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Copyright (C) 2016 by original authors @ fontello.com 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/fonts/my-custom-icon-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/fonts/my-custom-icon-font.ttf -------------------------------------------------------------------------------- /app/src/fonts/my-custom-icon-font.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/fonts/my-custom-icon-font.woff -------------------------------------------------------------------------------- /app/src/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/images/favicon.png -------------------------------------------------------------------------------- /app/src/images/github-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/images/github-1.png -------------------------------------------------------------------------------- /app/src/images/github-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/images/github-2.png -------------------------------------------------------------------------------- /app/src/images/github-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/app/src/images/github-3.png -------------------------------------------------------------------------------- /app/src/images/loading.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Wechat H5 Boilerplate 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 |
28 | 29 |
30 | 31 |
32 |
33 |
34 |
35 |

I'm a coder!

36 |
37 |
38 |
39 |

I'm a frontend developer!

40 |
41 |
42 |
43 |

I'm a javascript newbie!

44 |
45 |
46 |
47 | 48 | 51 | 52 | 55 | 56 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/javascripts/animation-control.js: -------------------------------------------------------------------------------- 1 | var $ = require('jquery'); 2 | 3 | module.exports = { 4 | initAnimationItems: function() { 5 | $('.animated').each(function () { 6 | var aniDuration, aniDelay; 7 | 8 | $(this).attr('data-origin-class', $(this).attr('class')); 9 | 10 | aniDuration = $(this).data('ani-duration'); 11 | aniDelay = $(this).data('ani-delay'); 12 | 13 | $(this).css({ 14 | 'visibility': 'hidden', 15 | 'animation-duration': aniDuration, 16 | '-webkit-animation-duration': aniDuration, 17 | 'animation-delay': aniDelay, 18 | '-webkit-animation-delay': aniDelay 19 | }); 20 | }); 21 | }, 22 | 23 | playAnimation: function (swiper) { 24 | this.clearAnimation(); 25 | 26 | var aniItems = swiper.slides[swiper.activeIndex].querySelectorAll('.animated'); 27 | 28 | $(aniItems).each(function () { 29 | var aniName; 30 | $(this).css({ 'visibility': 'visible' }); 31 | aniName = $(this).data('ani-name'); 32 | $(this).addClass(aniName); 33 | }); 34 | }, 35 | 36 | clearAnimation: function () { 37 | $('.animated').each(function () { 38 | $(this).css({ 'visibility': 'hidden' }); 39 | $(this).attr('class', $(this).data('origin-class')); 40 | }); 41 | } 42 | }; -------------------------------------------------------------------------------- /app/src/javascripts/main.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | // load dependencies 5 | var animationControl = require('./animation-control.js'); 6 | 7 | 8 | $(document).ready(function () { 9 | var bgMusic = $('audio').get(0); 10 | var $btnMusic = $('.btn-music'); 11 | var $upArrow = $('.up-arrow'); 12 | 13 | // background music control 14 | $btnMusic.click(function () { 15 | if (bgMusic.paused) { 16 | bgMusic.play(); 17 | $(this).removeClass('paused'); 18 | } else { 19 | bgMusic.pause(); 20 | $(this).addClass('paused'); 21 | } 22 | }); 23 | 24 | // init Swiper 25 | new Swiper('.swiper-container', { 26 | mousewheelControl: true, 27 | effect: 'coverflow', // slide, fade, coverflow or flip 28 | speed: 400, 29 | direction: 'vertical', 30 | fade: { 31 | crossFade: false 32 | }, 33 | coverflow: { 34 | rotate: 100, 35 | stretch: 0, 36 | depth: 300, 37 | modifier: 1, 38 | slideShadows: false // do disable shadows for better performance 39 | }, 40 | flip: { 41 | limitRotation: true, 42 | slideShadows: false // do disable shadows for better performance 43 | }, 44 | onInit: function (swiper) { 45 | animationControl.initAnimationItems(); // get items ready for animations 46 | animationControl.playAnimation(swiper); // play animations of the first slide 47 | }, 48 | onTransitionStart: function (swiper) { // on the last slide, hide .btn-swipe 49 | if (swiper.activeIndex === swiper.slides.length - 1) { 50 | $upArrow.hide(); 51 | } else { 52 | $upArrow.show(); 53 | } 54 | }, 55 | onTransitionEnd: function (swiper) { // play animations of the current slide 56 | animationControl.playAnimation(swiper); 57 | }, 58 | onTouchStart: function (swiper, event) { // mobile devices don't allow audios to play automatically, it has to be triggered by a user event(click / touch). 59 | if (!$btnMusic.hasClass('paused') && bgMusic.paused) { 60 | bgMusic.play(); 61 | } 62 | } 63 | }); 64 | 65 | // hide loading animation since everything is ready 66 | $('.loading-overlay').slideUp(); 67 | }); 68 | })(); 69 | -------------------------------------------------------------------------------- /app/src/scss/base/_base.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | html { 4 | width: 100%; 5 | height: 100%; 6 | position: relative; 7 | background-color: #000; 8 | font-size: 16px; 9 | } 10 | 11 | @media all and (min-width: 400px) { 12 | html { 13 | font-size: 22px; 14 | } 15 | } 16 | 17 | @media all and (min-width: 600px) { 18 | html { 19 | font-size: 32px; 20 | } 21 | } 22 | 23 | body { 24 | width: 100%; 25 | height: 100%; 26 | position: relative; 27 | } -------------------------------------------------------------------------------- /app/src/scss/base/_reset.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | * { 4 | box-sizing: border-box; 5 | } 6 | 7 | button, input, textarea, select { 8 | outline: none; 9 | } 10 | 11 | button, a { 12 | -webkit-tap-highlight-color: rgba(0,0,0,0); 13 | } -------------------------------------------------------------------------------- /app/src/scss/components/_btn-music.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .btn-music { 4 | width: 2rem; 5 | height: 2rem; 6 | background-color: transparent; 7 | position: fixed; 8 | top: 1.5rem; 9 | right: 1.5rem; 10 | z-index: 999; 11 | border: 0.125rem solid #fff; 12 | border-radius: 50%; 13 | padding: 0; 14 | animation: btnMusicAni 3s infinite linear; 15 | i { 16 | width: 2rem; 17 | height: 2rem; 18 | text-align: center; 19 | line-height: 2rem; 20 | color: #fff; 21 | font-size: 1rem; 22 | position: absolute; 23 | top: -0.125rem; 24 | left: -0.125rem; 25 | } 26 | &.paused { 27 | opacity: 0.4; 28 | animation: none; 29 | } 30 | } 31 | 32 | @keyframes btnMusicAni { 33 | 0% { 34 | transform: rotate(0deg); 35 | } 36 | 37 | 100% { 38 | transform: rotate(360deg); 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/scss/components/_icon-font.scss: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'my-custom-icon-font'; 3 | src: url('../fonts/my-custom-icon-font.eot?90099692'); 4 | src: url('../fonts/my-custom-icon-font.eot?90099692#iefix') format('embedded-opentype'), 5 | url('../fonts/my-custom-icon-font.woff?90099692') format('woff'), 6 | url('../fonts/my-custom-icon-font.ttf?90099692') format('truetype'), 7 | url('../fonts/my-custom-icon-font.svg?90099692#my-custom-icon-font') format('svg'); 8 | font-weight: normal; 9 | font-style: normal; 10 | } 11 | /* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */ 12 | /* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */ 13 | /* 14 | @media screen and (-webkit-min-device-pixel-ratio:0) { 15 | @font-face { 16 | font-family: 'my-custom-icon-font'; 17 | src: url('../font/my-custom-icon-font.svg?90099692#my-custom-icon-font') format('svg'); 18 | } 19 | } 20 | */ 21 | 22 | [class^="icon-"]:before, [class*=" icon-"]:before { 23 | font-family: "my-custom-icon-font"; 24 | font-style: normal; 25 | font-weight: normal; 26 | speak: none; 27 | 28 | display: inline-block; 29 | text-decoration: inherit; 30 | width: 1em; 31 | margin-right: .2em; 32 | text-align: center; 33 | /* opacity: .8; */ 34 | 35 | /* For safety - reset parent styles, that can break glyph codes*/ 36 | font-variant: normal; 37 | text-transform: none; 38 | 39 | /* fix buttons height, for twitter bootstrap */ 40 | line-height: 1em; 41 | 42 | /* Animation center compensation - margins should be symmetric */ 43 | /* remove if not needed */ 44 | margin-left: .2em; 45 | 46 | /* you can be more comfortable with increased icons size */ 47 | /* font-size: 120%; */ 48 | 49 | /* Font smoothing. That was taken from TWBS */ 50 | -webkit-font-smoothing: antialiased; 51 | -moz-osx-font-smoothing: grayscale; 52 | 53 | /* Uncomment for 3D effect */ 54 | /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */ 55 | } 56 | 57 | .icon-note:before { content: '\e800'; } /* '' */ 58 | .icon-angle-double-up:before { content: '\e801'; } /* '' */ -------------------------------------------------------------------------------- /app/src/scss/components/_loading-overlay.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .loading-overlay { 4 | position: fixed; 5 | width: 100%; 6 | height: 100%; 7 | background-color: #000; 8 | z-index: 9999; 9 | img { 10 | width: 10rem; 11 | height: 10rem; 12 | margin: auto; 13 | position: absolute; 14 | top: 0; 15 | bottom: 0; 16 | left: 0; 17 | right: 0; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /app/src/scss/components/_swiper-container.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .swiper-container { 4 | width: 100%; 5 | height: 100%; 6 | } -------------------------------------------------------------------------------- /app/src/scss/components/_swiper-slide.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .swiper-slide { 4 | overflow: hidden; 5 | background-size: cover; 6 | background-position: center; 7 | background-repeat: no-repeat; 8 | } -------------------------------------------------------------------------------- /app/src/scss/components/_up-arrow.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | .up-arrow { 4 | width: 2rem; 5 | height: 2rem; 6 | position: fixed; 7 | bottom: 2rem; 8 | left: 50%; 9 | transform: translate3d(-50%, 0, 0); 10 | z-index: 999; 11 | background: none; 12 | border: none; 13 | padding: 0; 14 | animation: upArrowAni 2s infinite linear; 15 | i { 16 | width: 2rem; 17 | height: 2rem; 18 | text-align: center; 19 | line-height: 2rem; 20 | color: #fff; 21 | font-size: 1.5rem; 22 | } 23 | } 24 | 25 | @keyframes upArrowAni { 26 | 0% { 27 | opacity: 0; 28 | transform: translate3d(-50%, 30%, 0); 29 | } 30 | 31 | 30% { 32 | opacity: 1; 33 | transform: translate3d(-50%, -20%, 0); 34 | } 35 | 36 | 60% { 37 | opacity: 0; 38 | transform: translate3d(-50%, -35%, 0); 39 | } 40 | 41 | 100% { 42 | opacity: 0; 43 | transform: translate3d(-50%, -50%, 0); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/scss/helpers/_functions.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; -------------------------------------------------------------------------------- /app/src/scss/helpers/_global-variables.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; -------------------------------------------------------------------------------- /app/src/scss/helpers/_mixins.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; -------------------------------------------------------------------------------- /app/src/scss/main.scss: -------------------------------------------------------------------------------- 1 | @charset "UTF-8"; 2 | 3 | // helpers 4 | @import "helpers/global-variables"; 5 | @import "helpers/mixins"; 6 | @import "helpers/functions"; 7 | 8 | // base 9 | @import "base/reset"; 10 | @import "base/base"; 11 | 12 | // components 13 | @import "components/loading-overlay"; 14 | @import "components/btn-music"; 15 | @import "components/up-arrow"; 16 | @import "components/swiper-container"; 17 | @import "components/swiper-slide"; 18 | @import "components/icon-font"; 19 | 20 | // sections 21 | @import "slides/slide-1"; 22 | @import "slides/slide-2"; 23 | @import "slides/slide-3"; 24 | -------------------------------------------------------------------------------- /app/src/scss/slides/_slide-1.scss: -------------------------------------------------------------------------------- 1 | .slide-1 { 2 | background-color: #e88a63; 3 | .item-image { 4 | width: 18rem; 5 | height: 18rem; 6 | background-position: center; 7 | background-size: contain; 8 | background-repeat: no-repeat; 9 | position: absolute; 10 | top: 4rem; 11 | left: 0; 12 | right: 0; 13 | margin: auto; 14 | background-image: url('../images/github-1.png'); 15 | } 16 | .item-text { 17 | width: 18rem; 18 | color: #fff; 19 | font-size: 1.2rem; 20 | font-weight: bold; 21 | position: absolute; 22 | top: 24rem; 23 | left: 0; 24 | right: 0; 25 | margin: 0 auto; 26 | text-align: center; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/scss/slides/_slide-2.scss: -------------------------------------------------------------------------------- 1 | .slide-2 { 2 | background-color: #5cc2f1; 3 | .item-image { 4 | width: 18rem; 5 | height: 18rem; 6 | background-position: center; 7 | background-size: contain; 8 | background-repeat: no-repeat; 9 | position: absolute; 10 | top: 4rem; 11 | left: 0; 12 | right: 0; 13 | margin: auto; 14 | background-image: url('../images/github-2.png'); 15 | } 16 | .item-text { 17 | width: 18rem; 18 | color: #fff; 19 | font-size: 1.2rem; 20 | font-weight: bold; 21 | position: absolute; 22 | top: 24rem; 23 | left: 0; 24 | right: 0; 25 | margin: 0 auto; 26 | text-align: center; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/scss/slides/_slide-3.scss: -------------------------------------------------------------------------------- 1 | .slide-3 { 2 | background-color: #9993c1; 3 | .item-image { 4 | width: 18rem; 5 | height: 18rem; 6 | background-position: center; 7 | background-size: contain; 8 | background-repeat: no-repeat; 9 | position: absolute; 10 | top: 4rem; 11 | left: 0; 12 | right: 0; 13 | margin: auto; 14 | background-image: url('../images/github-3.png'); 15 | } 16 | .item-text { 17 | width: 18rem; 18 | color: #fff; 19 | font-size: 1.2rem; 20 | font-weight: bold; 21 | position: absolute; 22 | top: 24rem; 23 | left: 0; 24 | right: 0; 25 | margin: 0 auto; 26 | text-align: center; 27 | } 28 | } -------------------------------------------------------------------------------- /config/vendors.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | stylesheets: [ 3 | 'node_modules/normalize.css/normalize.css', 4 | 'node_modules/swiper/dist/css/swiper.css', 5 | 'node_modules/animate.css/animate.css', 6 | // if you need font-awesome 7 | // node_modules/font-awesome/css/font-awesome.css 8 | 9 | ], 10 | 11 | javascripts: [ 12 | 'node_modules/jquery/dist/jquery.js', 13 | 'node_modules/swiper/dist/js/swiper.jquery.umd.js' 14 | ], 15 | 16 | fonts: [ 17 | // if you need font-awesome 18 | // 'node_modules/font-awesome/fonts/*' 19 | ] 20 | }; -------------------------------------------------------------------------------- /demo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/panteng/wechat-h5-boilerplate/8e9928bb9a96afe5c5a0845a2e53f5428713274e/demo.jpg -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var autoprefixer = require('gulp-autoprefixer'); 4 | var browserify = require('gulp-browserify'); 5 | var browserSync = require('browser-sync').create(); 6 | var concat = require('gulp-concat'); 7 | var del = require('del'); 8 | var gulp = require('gulp'); 9 | var imagemin = require('gulp-imagemin'); 10 | var inject = require('gulp-inject'); 11 | var minifycss = require('gulp-minify-css'); 12 | var notify = require('gulp-notify'); 13 | var rename = require('gulp-rename'); 14 | var runSequence = require('run-sequence'); 15 | var sass = require('gulp-sass'); 16 | var streamSeries = require('stream-series'); 17 | var plumber = require('gulp-plumber'); 18 | var uglify = require('gulp-uglify'); 19 | 20 | var vendors = require('./config/vendors'); 21 | 22 | 23 | 24 | /* ============================================================================================================ 25 | ============================================ For Development ================================================== 26 | =============================================================================================================*/ 27 | 28 | 29 | // copy fonts from node_modules and app/src/fonts to app/dist/fonts 30 | gulp.task('publish-fonts', function () { 31 | var fonts = vendors.fonts.concat([ 32 | 'app/src/fonts/*' 33 | ]); 34 | 35 | return gulp.src(fonts) 36 | .pipe(gulp.dest('app/dist/fonts')); 37 | }); 38 | 39 | // optimize images under app/src/images and save the results to app/dist/images 40 | gulp.task('publish-images', function () { 41 | var imagesWithoutSVG = ['app/src/images/**/*', '!app/src/images/**/*.svg']; 42 | var SVGs = 'app/src/images/**/*.svg'; 43 | 44 | return streamSeries( 45 | gulp.src(imagesWithoutSVG) 46 | .pipe(imagemin({ 47 | optimizationLevel: 3, 48 | progressive: true, 49 | interlaced: true 50 | })), 51 | gulp.src(SVGs) 52 | ) 53 | .pipe(gulp.dest('app/dist/images')); 54 | }); 55 | 56 | // copy audios from app/src/audios to app/dist/audios 57 | gulp.task('publish-audios', function () { 58 | return gulp.src('app/src/audios/*') 59 | .pipe(gulp.dest('app/dist/audios')); 60 | }); 61 | 62 | // compile sass, concat stylesheets in the right order, 63 | // and save as app/dist/stylesheets/bundle.css 64 | gulp.task('publish-css', function () { 65 | var cssVendors = vendors.stylesheets; 66 | 67 | return streamSeries( 68 | gulp.src(cssVendors), 69 | gulp.src('app/src/scss/main.scss') 70 | .pipe(plumber({ 71 | errorHandler: errorAlert 72 | })) 73 | .pipe(sass({ 74 | outputStyle: 'expanded' 75 | })) 76 | .pipe(autoprefixer()) 77 | ) 78 | .pipe(concat('bundle.css')) 79 | .pipe(gulp.dest('app/dist/stylesheets')) 80 | .pipe(browserSync.stream()); 81 | }); 82 | 83 | // bundle CommonJS modules under app/src/javascripts, concat javascripts in the right order, 84 | // and save as app/dist/javascripts/bundle.js 85 | gulp.task('publish-js', function () { 86 | var jsVendors = vendors.javascripts; 87 | 88 | return streamSeries( 89 | gulp.src(jsVendors), 90 | gulp.src('app/src/javascripts/main.js') 91 | .pipe(plumber({ 92 | errorHandler: errorAlert 93 | })) 94 | .pipe(browserify({ 95 | transform: ['partialify'], 96 | debug: true 97 | })) 98 | ) 99 | .pipe(concat('bundle.js')) 100 | .pipe(gulp.dest('app/dist/javascripts')); 101 | }); 102 | 103 | // inject app/dist/stylesheets/bundle.css and app/dist/javascripts/bundle.js into app/src/index.html 104 | // and save as app/dist/index.html 105 | gulp.task('inject', function () { 106 | var target = gulp.src('app/src/index.html'); 107 | var assets = gulp.src([ 108 | 'app/dist/stylesheets/bundle.css', 109 | 'app/dist/javascripts/bundle.js' 110 | ], { 111 | read: false 112 | }); 113 | return target 114 | .pipe(inject(assets, { 115 | ignorePath: 'app/dist/', 116 | addRootSlash: false, 117 | removeTags: true 118 | })) 119 | .pipe(gulp.dest('app/dist')); 120 | }); 121 | 122 | // watch files and run corresponding task(s) once files are added, removed or edited. 123 | gulp.task('watch', function () { 124 | browserSync.init({ 125 | server: { 126 | baseDir: 'app/dist' 127 | } 128 | }); 129 | 130 | gulp.watch('app/src/index.html', ['inject']); 131 | gulp.watch('app/src/scss/**/*.scss', ['publish-css']); 132 | gulp.watch('app/src/javascripts/**/*', ['publish-js']); 133 | gulp.watch('app/src/fonts/**/*', ['publish-fonts']); 134 | gulp.watch('app/src/images/**/*', ['publish-images']); 135 | gulp.watch('app/src/audios/**/*', ['publish-audios']); 136 | 137 | gulp.watch('app/dist/index.html').on('change', browserSync.reload); 138 | gulp.watch('app/dist/javascripts/*').on('change', browserSync.reload); 139 | gulp.watch('app/dist/fonts/*').on('change', browserSync.reload); 140 | gulp.watch('app/dist/images/*').on('change', browserSync.reload); 141 | }); 142 | 143 | // delete files under app/dist 144 | gulp.task('clean-files', function(cb) { 145 | return del([ 146 | 'app/dist/**/*' 147 | ], cb); 148 | }); 149 | 150 | // delete cache 151 | // gulp.task('clean-cache', function (cb) { 152 | // return cache.clearAll(cb) 153 | // }); 154 | 155 | // development workflow task 156 | gulp.task('dev', function (cb) { 157 | runSequence(['clean-files'], ['publish-fonts', 'publish-images', 'publish-audios', 'publish-css', 'publish-js'], 'inject', 'watch', cb); 158 | }); 159 | 160 | // default task 161 | gulp.task('default', ['dev']); 162 | 163 | 164 | 165 | /* ============================================================================================================ 166 | ================================================= For Production ============================================== 167 | =============================================================================================================*/ 168 | 169 | // minify app/dist/stylesheets/bundle.css and save as app/dist/stylesheets/bundle.min.css 170 | gulp.task('minify-css', function () { 171 | return gulp.src('app/dist/stylesheets/bundle.css') 172 | .pipe(minifycss()) 173 | .pipe(rename({ 174 | suffix: '.min' 175 | })) 176 | .pipe(gulp.dest('app/dist/stylesheets')); 177 | }); 178 | 179 | // uglify app/dist/javascripts/bundle.js and save as app/dist/javascripts/bundle.min.js 180 | gulp.task('uglify-js', function () { 181 | return gulp.src('app/dist/javascripts/bundle.js') 182 | .pipe(uglify()) 183 | .pipe(rename({ 184 | suffix: '.min' 185 | })) 186 | .pipe(gulp.dest('app/dist/javascripts')); 187 | }); 188 | 189 | // inject app/dist/stylesheets/bundle.min.css and app/dist/javascripts/bundle.min.js into app/src/index.html 190 | // and save as app/dist/index.html 191 | gulp.task('inject-min', function () { 192 | var target = gulp.src('app/src/index.html'); 193 | var assets = gulp.src([ 194 | 'app/dist/stylesheets/bundle.min.css', 195 | 'app/dist/javascripts/bundle.min.js' 196 | ], { 197 | read: false 198 | }); 199 | return target 200 | .pipe(inject(assets, { 201 | ignorePath: 'app/dist/', 202 | addRootSlash: false, 203 | removeTags: true 204 | })) 205 | .pipe(gulp.dest('app/dist')); 206 | }); 207 | 208 | // delete app/dist/stylesheets/bundle.css and app/dist/javascripts/bundle.js 209 | gulp.task('del-bundle', function (cb) { 210 | return del([ 211 | 'app/dist/stylesheets/bundle.css', 212 | 'app/dist/javascripts/bundle.js' 213 | ], cb); 214 | }); 215 | 216 | // run 'minify-css' and 'uglify-js' at the same time 217 | // inject the minified files to index.html 218 | // delete unminified files 219 | gulp.task('prod', function (cb) { 220 | runSequence(['minify-css', 'uglify-js'], ['inject-min', 'del-bundle'], cb); 221 | }); 222 | 223 | 224 | 225 | /* =============================================== 226 | ================== Functions ==================== 227 | ================================================*/ 228 | 229 | // handle errors 230 | function errorAlert(error){ 231 | notify.onError({ 232 | title: "Error in plugin '" + error.plugin + "'", 233 | message: 'Check your terminal', 234 | sound: 'Sosumi' 235 | })(error); 236 | console.log(error.toString()); 237 | this.emit('end'); 238 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "wechat-h5-boilerplate", 3 | "version": "0.3.6", 4 | "description": "A fullpage scrolling website boilerplate for mobile devices.", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/panteng/wechat-h5-boilerplate.git" 8 | }, 9 | "author": "Chris Pan", 10 | "license": "MIT", 11 | "devDependencies": { 12 | "browser-sync": "^2.10.1", 13 | "del": "^2.2.0", 14 | "gulp": "^3.9.0", 15 | "gulp-autoprefixer": "^3.1.0", 16 | "gulp-browserify": "^0.5.1", 17 | "gulp-concat": "^2.6.0", 18 | "gulp-imagemin": "^3.1.1", 19 | "gulp-inject": "^4.2.0", 20 | "gulp-minify-css": "^1.2.2", 21 | "gulp-notify": "^3.0.0", 22 | "gulp-plumber": "^1.0.1", 23 | "gulp-rename": "^1.2.2", 24 | "gulp-sass": "^3.1.0", 25 | "gulp-uglify": "^2.0.1", 26 | "partialify": "^3.1.5", 27 | "run-sequence": "^1.1.5", 28 | "stream-series": "^0.1.1" 29 | }, 30 | "dependencies": { 31 | "animate.css": "^3.4.0", 32 | "jquery": "^3.1.1", 33 | "normalize.css": "^5.0.0", 34 | "swiper": "^3.2.7" 35 | } 36 | } 37 | --------------------------------------------------------------------------------