准备工作:
1、在微信公众号平台,申请小程序账号,获取appid
2、下载并安装微信开发者工具
3、做不同分辨率设备的自适应:单位使用rpx
IPhone6下 1px=1rpx=0.5pt
使用rpx,小程序会自动在不同分辨率下进行转换
首先是项目的入口页面
welcome.wxml
<view class="container"> <image class="avatar" src="/images/avatar/1.png">image> <text class="motto">Hello, 七月text> <view class="journey-container" bindtap="onTap"> <text class="journey">开启小程序之旅text> view> view>
welcome.wxss
.container{ display: flex; flex-direction:column; align-items: center; background-color:#b3d4db; } .avatar{ width:200rpx; height:200rpx; margin-top:160rpx; } .motto{ margin-top:100rpx; font-size:32rpx; font-weight: bold; } .journey-container{ margin-top: 200rpx; border: 1px solid #405f80; width: 200rpx; height: 80rpx; border-radius: 5px; text-align:center; } .journey{ font-size:22rpx; font-weight: bold; line-height:80rpx; color: #405f80; } page{ height: 100%; background-color:#b3d4db; }
welcome.js
Page({ onTap: function (event) { // wx.navigateTo({ // url:"../posts/post" // }); wx.switchTab({ url: "../posts/post" }); }, onReachBottom:function(event){ console.log('asfasdfa') } })
welcome.json(主要是设置最上面的导航栏的颜色)
{
"navigationBarBackgroundColor": "#b3d4db"
}
接下来是新闻列表页
这里是把循环的每条新闻的结构独立出来,到post-item文件夹中
post-item-template.wxml
<template name="postItem"> <view class="post-container"> <view class="post-author-date"> <image class="post-author" src="{{avatar}}">image> <text class="post-date">{{date}}text> view> <text class="post-title">{{title}}text> <image class="post-image" src="{{imgSrc}}">image> <text class="post-content">{{content}} text> <view class="post-like"> <image class="post-like-image" src="/images/icon/chat.png">image> <text class="post-like-font">{{collection}}text> <image class="post-like-image" src="/images/icon/view.png">image> <text class="post-like-font">{{reading}}text> view> view> template>
post-item-template.wxss
.post-container{ flex-direction:column; display:flex; margin-top:20rpx; margin-bottom:40rpx; margin-left: 0rpx; background-color:#fff; border-bottom: 1px solid #ededed; border-top: 1px solid #ededed; padding-bottom: 5px; } .post-author-date{ margin-top:10rpx; margin-bottom: 20rpx; margin-left: 10px; } .post-author{ width:60rpx; height:60rpx; vertical-align: middle; } .post-date{ margin-left: 20px; vertical-align: middle; } .post-image{ width:100%; height:340rpx; margin:auto 0; margin-bottom: 15px; } .post-date{ font-size:26rpx; margin-bottom: 10px; } .post-title{ font-size:34rpx; font-weight: 600; color:#333; margin-bottom: 10px; margin-left: 10px; } .post-content{ color:#666; font-size:28rpx; margin-bottom:20rpx; margin-left: 20rpx; letter-spacing:2rpx; line-height: 40rpx; } .post-like{ font-size:13px; line-height: 16px; margin-left: 10px; } .post-like-image{ height:16px; width:16px; margin-right: 8px; vertical-align:middle; } .post-like-font{ vertical-align:middle; margin-right: 20px; }
post.wxml
<import src="post-item/post-item-template.wxml" /> <view> <swiper catchtap="onSwiperTap" vertical="{{false}}" indicator-dots="true" autoplay="true" interval="5000"> <swiper-item> <image id="7" src="/images/wx.png" data-postId="3">image> swiper-item> <swiper-item> <image src="/images/vr.png" data-postId="4">image> swiper-item> <swiper-item> <image src="/images/iqiyi.png" data-postId="5">image> swiper-item> swiper> <block wx:for="{{postList}}" wx:for-item="item" wx:for-index="idx"> <view catchtap="onPostTap" data-postId="{{item.postId}}"> <template is="postItem" data="{{...item}}"/> view> block> view>
post.wxss
@import "post-item/post-item-template.wxss"; swiper{ width:100%; height:600rpx; } /*less sass*/ swiper image{ width:100%; height:600rpx; }
post.js
var postsData = require('../../data/posts-data.js') Page({ data: { //小程序总是会读取data对象来做数据绑定,这个动作我们称为动作A // 而这个动作A的执行,是在onLoad函数执行之后发生的 }, onLoad: function () { // this.data.postList = postsData.postList this.setData({ postList:postsData.postList }); }, onReachBottom:function(event){ console.log('asdfasdfasdf') }, onPostTap: function (event) { var postId = event.currentTarget.dataset.postid; // console.log("on post id is" + postId); wx.navigateTo({ url: "post-detail/post-detail?id=" + postId }) }, onSwiperTap: function (event) { // target 和currentTarget // target指的是当前点击的组件 和currentTarget 指的是事件捕获的组件 // target这里指的是image,而currentTarget指的是swiper var postId = event.target.dataset.postid; wx.navigateTo({ url: "post-detail/post-detail?id=" + postId }) } })
post.json
{ "navigationBarTitleText":"文与字" }
然后是新闻详情页
post-detail.wxml
<view class="container"> <image class="head-image" src="{{isPlayingMusic?postData.music.coverImg:postData.headImgSrc}}">image> <image catchtap="onMusicTap" class="audio" src="{{isPlayingMusic? '/images/music/music-stop.png': '/images/music/music-start.png'}}">image> <view class="author-date"> <image class="avatar" src="{{postData.avatar}}">image> <text class="author">{{postData.author}}text> <text class="const-text">发表于text> <text class="date">{{postData.dateTime}}text> view> <text class="title">{{postData.title}}text> <view class="tool"> <view class="circle-img"> <image wx:if="{{collected}}" catchtap="onColletionTap" src="/images/icon/collection.png">image> <image wx:else catchtap="onColletionTap" src="/images/icon/collection-anti.png">image> <image catchtap="onShareTap" class="share-img" src="/images/icon/share.png">image> view> <view class="horizon">view> view> <text class="detail">{{postData.detail}}text> view>
post-detail.wxss
.container { display: flex; flex-direction: column; } .head-image { width: 100%; height: 460rpx; } .hide{ opacity: 0; } .audio { width: 102rpx; height: 110rpx; position: absolute; left: 50%; margin-left: -51rpx; top: 180rpx; opacity: 0.6; } .author-date { flex-direction: row; margin-left: 30rpx; margin-top: 20rpx; } .avatar { height: 64rpx; width: 64rpx; vertical-align: middle; } .author { font-size: 30rpx; font-weight: 300; margin-left: 20rpx; vertical-align: middle; color: #666; } .const-text { font-size: 24rpx; color: #999; margin-left: 20rpx; } .date { font-size: 24rpx; margin-left: 30rpx; vertical-align: middle; color: #999; } .title { margin-left: 40rpx; font-size: 36rpx; font-weight: 700; margin-top: 30rpx; letter-spacing: 2px; color: #4b556c; } .tool { margin-top: 20rpx; } .circle-img { float: right; margin-right: 40rpx; vertical-align: middle; } .circle-img Image { width: 90rpx; height: 90rpx; } .share-img { margin-left: 30rpx; } .horizon { width: 660rpx; height: 1px; background-color: #e5e5e5; vertical-align: middle; position: relative; top: 46rpx; margin: 0 auto; z-index: -99; } .detail { color: #666; margin-left: 30rpx; margin-top: 20rpx; margin-right: 30rpx; line-height: 44rpx; letter-spacing: 2px; }
post-detail.js
var postsData = require('../../../data/posts-data.js') var app = getApp(); Page({ data: { isPlayingMusic: false }, onLoad: function (option) { var postId = option.id; this.data.currentPostId = postId; var postData = postsData.postList[postId]; this.setData({ postData: postData }) var postsCollected = wx.getStorageSync('posts_collected') if (postsCollected) { var postCollected = postsCollected[postId] if (postCollected){ this.setData({ collected: postCollected }) } } else { var postsCollected = {}; postsCollected[postId] = false; wx.setStorageSync('posts_collected', postsCollected); } if (app.globalData.g_isPlayingMusic && app.globalData.g_currentMusicPostId === postId) { this.setData({ isPlayingMusic: true }) } this.setMusicMonitor(); }, setMusicMonitor: function () { //点击播放图标和总控开关都会触发这个函数 var that = this; wx.onBackgroundAudioPlay(function (event) { var pages = getCurrentPages(); var currentPage = pages[pages.length - 1]; if (currentPage.data.currentPostId === that.data.currentPostId) { // 打开多个post-detail页面后,每个页面不会关闭,只会隐藏。通过页面栈拿到到 // 当前页面的postid,只处理当前页面的音乐播放。 if (app.globalData.g_currentMusicPostId == that.data.currentPostId) { // 播放当前页面音乐才改变图标 that.setData({ isPlayingMusic: true }) } // if(app.globalData.g_currentMusicPostId == that.data.currentPostId ) // app.globalData.g_currentMusicPostId = that.data.currentPostId; } app.globalData.g_isPlayingMusic = true; }); wx.onBackgroundAudioPause(function () { var pages = getCurrentPages(); var currentPage = pages[pages.length - 1]; if (currentPage.data.currentPostId === that.data.currentPostId) { if (app.globalData.g_currentMusicPostId == that.data.currentPostId) { that.setData({ isPlayingMusic: false }) } } app.globalData.g_isPlayingMusic = false; // app.globalData.g_currentMusicPostId = null; }); wx.onBackgroundAudioStop(function () { that.setData({ isPlayingMusic: false }) app.globalData.g_isPlayingMusic = false; // app.globalData.g_currentMusicPostId = null; }); }, onColletionTap: function (event) { // this.getPostsCollectedSyc(); this.getPostsCollectedAsy(); }, getPostsCollectedAsy: function () { var that = this; wx.getStorage({ key: "posts_collected", success: function (res) { var postsCollected = res.data; var postCollected = postsCollected[that.data.currentPostId]; // 收藏变成未收藏,未收藏变成收藏 postCollected = !postCollected; postsCollected[that.data.currentPostId] = postCollected; that.showToast(postsCollected, postCollected); } }) }, getPostsCollectedSyc: function () { var postsCollected = wx.getStorageSync('posts_collected'); var postCollected = postsCollected[this.data.currentPostId]; // 收藏变成未收藏,未收藏变成收藏 postCollected = !postCollected; postsCollected[this.data.currentPostId] = postCollected; this.showToast(postsCollected, postCollected); }, showModal: function (postsCollected, postCollected) { var that = this; wx.showModal({ title: "收藏", content: postCollected ? "收藏该文章?" : "取消收藏该文章?", showCancel: "true", cancelText: "取消", cancelColor: "#333", confirmText: "确认", confirmColor: "#405f80", success: function (res) { if (res.confirm) { wx.setStorageSync('posts_collected', postsCollected); // 更新数据绑定变量,从而实现切换图片 that.setData({ collected: postCollected }) } } }) }, showToast: function (postsCollected, postCollected) { // 更新文章是否的缓存值 wx.setStorageSync('posts_collected', postsCollected); // 更新数据绑定变量,从而实现切换图片 this.setData({ collected: postCollected }) wx.showToast({ title: postCollected ? "收藏成功" : "取消成功", duration: 1000, icon: "success" }) }, onShareTap: function (event) { var itemList = [ "分享给微信好友", "分享到朋友圈", "分享到QQ", "分享到微博" ]; wx.showActionSheet({ itemList: itemList, itemColor: "#405f80", success: function (res) { // res.cancel 用户是不是点击了取消按钮 // res.tapIndex 数组元素的序号,从0开始 wx.showModal({ title: "用户 " + itemList[res.tapIndex], content: "用户是否取消?" + res.cancel + "现在无法实现分享功能,什么时候能支持呢" }) } }) }, onMusicTap: function (event) { var currentPostId = this.data.currentPostId; var postData = postsData.postList[currentPostId]; var isPlayingMusic = this.data.isPlayingMusic; if (isPlayingMusic) { wx.pauseBackgroundAudio(); this.setData({ isPlayingMusic: false }) // app.globalData.g_currentMusicPostId = null; app.globalData.g_isPlayingMusic = false; } else { wx.playBackgroundAudio({ dataUrl: postData.music.url, title: postData.music.title, coverImgUrl: postData.music.coverImg, }) this.setData({ isPlayingMusic: true }) app.globalData.g_currentMusicPostId = this.data.currentPostId; app.globalData.g_isPlayingMusic = true; } }, /* * 定义页面分享函数 */ onShareAppMessage: function (event) { return { title: '离思五首·其四', desc: '曾经沧海难为水,除却巫山不是云', path: '/pages/posts/post-detail/post-detail?id=0' } } })
post-detail.json
{ "navigationBarTitleText":"阅读" }
贴一下模拟的新闻数据posts-data.js
var local_database = [ { date: "Sep 18 2016", title: "正是虾肥蟹壮时", imgSrc: "/images/post/crab.png", avatar: "/images/avatar/1.png", content: "菊黄蟹正肥,品尝秋之味。徐志摩把,“看初花的荻芦”和“到楼外楼吃蟹”,并列为秋天来杭州不能错过的风雅之事;用林妹妹的话讲是“螯封嫩玉双双满,", reading: "112", collection: "96", headImgSrc: "/images/post/crab.png", author: "林白衣", dateTime: "24小时前", detail: "菊黄蟹正肥,品尝秋之味。徐志摩把“看初花的荻芦”和“到楼外楼吃蟹”并列为秋天来杭州不能错过的风雅之事;用林妹妹的话讲是“螯封嫩玉双双满,壳凸红脂块块香”;在《世说新语》里,晋毕卓更是感叹“右手持酒杯,左手持蟹螯,拍浮酒船中,便足了一生矣。”漫漫人生长路,美食与爱岂可辜负?于是作为一个吃货,突然也很想回味一下属于我的味蕾记忆。记忆中的秋蟹,是家人的味道,弥漫着浓浓的亲情。\n\n是谁来自山川湖海,却囿于昼夜,厨房与爱? 是母亲,深思熟虑,聪明耐心。吃蟹前,总会拿出几件工具,煞有介事而乐此不疲。告诉我们螃蟹至寒,需要佐以姜茶以祛寒,在配备的米醋小碟里,亦添入姜丝与紫苏,前者驱寒后者增香。泡好菊花茶,岁月静好,我们静等。", postId: 0, music: { url: "http://music.163.com/song/media/outer/url?id=142604.mp3", title: "夜夜夜夜-齐秦", coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000001TEc6V0kjpVC.jpg?max_age=2592000" } }, { title: "比利·林恩的中场故事", content: "一 “李安是一位绝不会重复自己的导演,本片将极富原创性李安众所瞩目的新片《比利林恩漫长的中场休息》,正式更名《半场无战事》。", imgSrc: "/images/post/bl.png", reading: 62, detail: "一 “李安是一位绝不会重复自己的导演,本片将极富原创性”李安众所瞩目的新片《比利林恩漫长的中场休息》,正式更名《半场无战事》。预告片首次曝光后,被视作是明年奥斯卡种子选手。该片根据同名畅销书改编。原著小说荣获美国国家图书奖。也被BBC评为21世纪最伟大的12本英文小说之一。影片讲述一位19岁德州男孩的比利·林恩入伍参加伊战,在一次交火中他大难不死,意外与战友成为大众的关注焦点,并被塑造成英雄。之后他们返回国内,在橄榄球赛中场休息时授勋。这名战争英雄却面临前所未有的心灵煎熬……李安为什么选中这部电影来拍?因为李安想要挑战前所未有的技术难题:以120帧每秒的速度、4K、3D技术全面结合,来掀起一场电影视觉革命。什么意思?所谓“电影是24格每秒的谎言”,其中的24格,就是帧数。", collection: 92, dateTime: "24小时前", headImgSrc: "/images/post/bl.png", author: "迷的城", date: "Nov 20 2016", avatar: "/images/avatar/1.png", postId: 1, music: { url: "http://music.163.com/song/media/outer/url?id=108220.mp3", title: "鬼迷心窍-李宗盛", coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000002xOmp62kqSic.jpg?max_age=2592000" } }, { //按住alt + shift + F 可以格式化代码样式 title: "当我们在谈论经济学时,我们在谈论什么?", content: "引言在我跟学生课后交流时,以及我在知乎上阅读有关“经济”问题的论题时,经常会遇到这样的情况:...", detail: "1 引言\n\n在我跟学生课后交流时,以及我在知乎上阅读有关“经济”问题的论题时,经常会遇到这样的情况:有些人套用“经济理论“的知识去解释现实中发生的经济事件,结果发现很多事情讲不通,或者发现”理论告诉我们的“与现实发生的是相反的。也有学生经常跟我说:经济学有什么用?为了说明这个,我经常从两个方面来进行解释,尝试用我个人所擅长的解决问题的视角和他们能够听懂的方法来说明经济学是什么,它的作用边界在哪里:\r\n\n2 ”简笔素描“与”油画肖像“我们给人画肖像画,可以用简笔素描,也可以用油画肖像。油画肖像可以在最大程度上保存了人物的各方面的细节和特点,而简笔素描则忽略了很多细节。尽管简笔素描忽略了人物的许多细节,但我们仍旧能够很容易的认出画中的人物是谁。为什么?因为这种方法保留了人物最显著的特征,以至于我们可以忽略其次要特征而对人物做出判定。\n\n2.1 ”简笔素描“对于绝大多数的非经济学专业大众而言(经济学相关专业硕士学历以上),人们所接触到的经济学都是初级微观经济学。所谓的初级微观经济学,对于经济问题的”画法“就是一种”简笔素描“。比如初级微观经济学教材中广为使用的这种一元一次需求函数:y=bx+a,需求量的唯一变量是产品价格。但仅凭直觉我们就可以断言,现实中影响需求量的因素绝不止价格这一种,因此我们可以认为这个模型对经济问题的描述是失真的。然而但这种失真却是必要的和有意义的,其意义在与它利于揭示价格对于需求的影响,而不在于否定影响需求的其他因素——", imgSrc: "/images/post/sls.jpg", headImgSrc: "/images/post/sls.jpg", reading: 62, collection: 92, author: "知乎", date: "Nov 12 2016", dateTime: "三天前", avatar: "/images/avatar/3.png", postId: 2, music: { url: "http://music.163.com/song/media/outer/url?id=27538254.mp3", title: "女儿情-万晓利", coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000004Wv5BO30pPc0.jpg?max_age=2592000" } }, { title: "微信·小程序开发工具安装指南", content: "这两天闲来无事,也安装了 “微信折叠”的开发工具来玩一下。以下是一些小道消息及使用体验,过两天我会写一篇文章以开发者的角度来详细评价微信小程序", imgSrc: "/images/post/xiaolong.jpg", reading: 102, detail: "这两天闲来无事,也安装了 “微信折叠”的开发工具来玩一下。以下是一些小道消息及使用体验,过两天我会写一篇文章以开发者的角度来详细评价微信小程序:微信小程序不能开发游戏类、直播类功能,小程序每个人关注的上限是20个(还不确定,不过我相信这是真的,这次公布的API里并没有视频组件。微信太大,苹果要有所顾忌,但是微信也要做出相应的让步)微信目前有没有同苹果商谈好,还是个未知数,毕竟会对AppStore有一定的冲击。抛弃了大量的javascript组件后,这个生态体系变得相当的封闭,微信解释肯定是:为了更好的性能提升。那么我们拭目以待。小程序的入口是微信里的三级菜单,就是在“Tab栏发现里的游戏下面加入一个“小程序”。反正,这一栏里的购物和游戏我是从来没点进去过的。以腾讯的尿性,小程序同服务号一样,其关系链及重要功能的开放程度会因“人”而异。对,优质的接口只会开放给腾讯的儿子们(滴滴呀、京东呀)", collection: 92, dateTime: "24小时前", headImgSrc: "/images/post/xiaolong.jpg", author: "猫是猫的猫", date: "Nov 20 2016", avatar: "/images/avatar/5.png", postId: 3, music: { url: "http://music.163.com/song/media/outer/url?id=108119.mp3", title: "恋恋风尘-老狼", coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000001VaXQX1Z1Imq.jpg?max_age=2592000", } }, { title: "从视觉到触觉 这款VR手套能给你真实触感", content: "8月29日消息,据国外媒体VentureBeat报道,一家名为Dexta Robotics的公司最近发布了一款有望变革虚拟现实手部追踪与交互方式的新产品", imgSrc: "/images/post/vr.png", reading: 102, detail: "消息,据国外媒体VentureBeat报道,一家名为Dexta Robotics的公司最近发布了一款有望变革虚拟现实手部追踪与交互方式的新产品。该产品名为“Dexmo”,它是一款像手套那样戴在手上使用的未来主义外骨骼。它内置大量的元件,能够与VR体验进行交互,可帮助你感觉握在你的双手的虚拟物体。Dexmo据Dexta称,“Dexmo是一款针对你的双手的机械外骨骼。它能够捕捉你的手部运动,以及提供即时的力反馈。有了Dexmo,你可以感受到虚拟物体的大小、形状和坚硬度。你可以接触数字世界。”市面上已经有数款产品旨在处理虚拟现实中的手部交互,也有相关的产品即将要进入市场。例如,颇受欢迎的HTC Vive头盔配有一副控制器,其控制器能够使得追踪系统看到你的双手,让你可以用它们来在特定体验中与物体进行交互。今年晚些时候,Oculus将开始出货类似的手部控制产品Oculus Touch。10月,索尼也将开始出货配备两个PlayStation Move手部控制器的PS VR。Leap Motion甚至更进一步:利用传感器来追踪手指和手部的运动。", collection: 26, dateTime: "24小时前", headImgSrc: "/images/post/vr.png", author: "深白色", date: "Nov 20 2016", avatar: "../../../images/avatar/3.png", postId: 4, music: { url: "http://music.163.com/song/media/outer/url?id=188204.mp3", title: "沉默是金-张国荣", coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000003at0mJ2YrR2H.jpg?max_age=2592000" } }, { title: "爱奇艺创维开展战略合作,合力布局开放娱乐生态", content: "爱奇艺和创维分别作为国内领先的在线视频品牌", imgSrc: "/images/iqiyi.png", reading: 96, detail: "爱奇艺和创维分别作为国内领先的在线视频品牌和家电品牌。双方一直锐意创新,为用户提供优质的服务体验和产品体验。据悉,爱奇艺与创维将展开从资本到VIP会员服务等各方面的深入合作。籍由此次合作,爱奇艺将战略投资创维旗下拥有高端互联网电视品牌的酷开公司。从下一财年开始,创维旗下互联网电视将通过银河互联网电视集成播控平台,预置VIP会员服务及相关内容。这种捆绑终端与VIP内容的全新销售模式,将大幅提升互联网电视终端用户的体验,给予用户更多优质内容的选择。", collection: 26, dateTime: "21小时前", headImgSrc: "/images/iqiyi.png", author: "深白色", date: "Nov 20 2016", avatar: "../../../images/avatar/5.png", postId: 5, music: { url: "http://music.163.com/song/media/outer/url?id=152428.mp3", title: "朋友-谭咏麟", coverImg: "http://y.gtimg.cn/music/photo_new/T002R150x150M000004eGsCN3SUheO.jpg?max_age=2592000" } }, ] module.exports = { postList: local_database }
最后是全局相关配置
app.json
{ "pages": [ "pages/welcome/welcome", "pages/posts/post", "pages/posts/post-detail/post-detail" ], "window": { "navigationBarBackgroundColor": "#405f80" }, "tabBar": { "borderStyle": "white", "position": "bottom", "list": [ { "pagePath": "pages/posts/post", "text": "阅读", "iconPath": "images/tab/yuedu.png", "selectedIconPath": "images/tab/yuedu_hl.png" }, { "pagePath": "pages/posts/post", "text": "电影", "iconPath": "images/tab/yuedu.png", "selectedIconPath": "images/tab/yuedu_hl.png" } ] } }
app.wxss
text{ font-family: MicroSoft Yahei; font-size: 24rpx; color: #666; } input{ font-family: MicroSoft YaHei; }