微信小程序 最新推出的Skyline渲染以及打包ios、andorid查看各个技术平台很少有详细介绍与项目案例,下面是一个简单的壁纸项目开发实战案例,涵盖了从项目创建到功能实现的整个过程。
项目概述
我们开发的是一个小程序手机、平板、pc端壁纸项目。用户可以点赞、收藏、下载壁纸。虽然项目很简单,但项目中我们会涉及多语言、主题外观、适配ios andorid、手势系统、自定义tabbar、自定义appbar、复杂组件开发测试、自定义路由、 元素共享动画、以及一些算法如:发布订阅、线性插值、矩形插值等高阶内容。
微信小程序 Skyline Worklet ios Andorid 项目实战-01的视频
自定义tabbat
自定义appbar
因为小程序的appbar比较特殊,所以建议在appbar上使用px而不是rpx,应为rpx是逻辑像素,它不管设备的真实的像素,一刀切的把屏幕宽度分成750份,一份就是1rpx,这样的后果是在手机上看上去一切正常,如果使用平板打开 样式会乱掉。当然我们也可以 写一个rpx和px互换的工具方法,把px转成rpx后再使用也是可以的。util.js 工具类加入一下方法
/**
* rpx或px相互转换
* @param { String } unit 带单位的尺寸 rpx、px
* @param { Number } screenWidth 屏幕宽度不是设备可用的宽度
* @return { Number } 转换后的rpxn或px
*/
export const switchPxRx = (unit, screenWidth) => {
if (!unit || typeof (unit) !== 'string') return new Error('带单位的尺寸叁数错误')
if (!screenWidth || typeof (screenWidth) !== 'number') return new Error('设备宽度错误')
const value = parseInt(unit, 10)
const and = 0 - Number(unit.length - (value.toString().length))
const type = unit.slice(and)
if (type === 'px') return value / (screenWidth / 750)
if (type === 'rpx') return value * screenWidth / 750
}
rpx和px互换的工具方法
容器组件iAppbar
项目根目录创建一个components文件夹 用来存放我们的所有组件。创建出一个appbar头部容器组件名字叫iAppbar
const app = getApp()
const wininfo = app.globalData.windowInfo
/**
* 这是一个头部容器组件 主要以微信小程序胶囊作为计算基点, 分左中有三部分。
*/
Component({
options: {
addGlobalClass: true, // 使用全局样式
virtualHost: true, // 虚拟化节点
multipleSlots: true // 组件使用多个slot 必须配置 true 默认组件只能有一个slot
},
data: {
contentStyle: `height: ${wininfo.clientRect.height}px`,
wrapStyle: `height: ${wininfo.appbarHeight}px; padding-top:${wininfo.statusBarHeight}px; padding:${wininfo.statusBarHeight}px ${wininfo.padding}px 0 ${wininfo.padding}px`,
rectStyle: `flex: ${wininfo.clientRect.width}px 0 0; height: ${wininfo.clientRect.height}px; border-radius: ${wininfo.clientRect.height * 0.5}px`,
}
})
<view class="wrap" style="{{wrapStyle}}">
<view style="{{rectStyle}}">
<slot name="left"></slot>
</view>
<view class="content" style="{{contentStyle}}">
<slot></slot>
</view>
<view style="{{rectStyle}}">
<slot name="right"></slot>
</view>
</view>
.wrap {
width: 100vw;
display: flex;
align-items: center;
box-sizing: border-box;
background-color: transparent;
}
.content {
flex: 1;
height: 100%;
}
iAppbar是一个容器组件 设置 (virtualHost: true)虚拟化节点后不可在组件上使用样式 如下面写法是错误的,将没有效果:
<i-appbar style="width: 750rpx; height: 100rpx"></i-appbar>
头部容器组件
微信小程序引入字体图标
头部通用主组需要使用iconfont图片所有先引入图标 iconfont 我们在项目根目录创建 iconfont.wxss直接复制iconfont上项目的.css到iconfont.wxss, 再再app.wxss中引入字体 iconfont.wxss如下:
@font-face {
font-family: "iconfont"; /* Project id 4848352 */
src: url('//at.alicdn.com/t/c/font_4848352_f3cl6piqgkt.woff2?t=1742017152673') format('woff2'),
url('//at.alicdn.com/t/c/font_4848352_f3cl6piqgkt.woff?t=1742017152673') format('woff'),
url('//at.alicdn.com/t/c/font_4848352_f3cl6piqgkt.ttf?t=1742017152673') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
}
.i-meiyoushuju01::before {
content: "\e627";
}
.i-guanbi1::before {
content: "\e6f2";
}
.i-zujian::before {
content: "\e61f";
}
.i-pianhaoshezhi-bai2::before {
content: "\eaf1";
}
.i-baohumoshi::before {
content: "\e63b";
}
.i-zhanghaoxiangguan::before {
content: "\e698";
}
.i-duoyuyan::before {
content: "\e654";
}
.i-zhutise::before {
content: "\e643";
}
.i-xiazai1::before {
content: "\e62e";
}
.i-shanchu::before {
content: "\e74b";
}
.i-gouxuan::before {
content: "\e631";
}
.i-anquanyinsi::before {
content: "\e98b";
}
.i-fankui::before {
content: "\e623";
}
.i-tubiao-::before {
content: "\e62c";
}
.i-kefu1::before {
content: "\e611";
}
.i-guanyu::before {
content: "\e7e8";
}
.i-gengxin::before {
content: "\e650";
}
.i-dianzan_kuai::before {
content: "\ec8c";
}
.i-good::before {
content: "\e62a";
}
.i-xihuan::before {
content: "\e86f";
}
.i-xihuan1::before {
content: "\e870";
}
.i-zhifeiji::before {
content: "\e62d";
}
.i-shoucang::before {
content: "\e613";
}
.i-xz::before {
content: "\e603";
}
.i-haibao::before {
content: "\e635";
}
.i-icon_386::before {
content: "\e60f";
}
.i-xiazai::before {
content: "\e660";
}
.i-fanhui1::before {
content: "\e604";
}
.i-a-chexiaofanhui::before {
content: "\e83d";
}
.i-quxian::before {
content: "\e694";
}
.i-kefu::before {
content: "\ec2e";
}
.i-saoyisao::before {
content: "\e610";
}
.i-shezhi::before {
content: "\e63a";
}
.i-mobile::before {
content: "\e622";
}
.i-tablet::before {
content: "\e670";
}
.i-leibie::before {
content: "\e632";
}
.i-discoverbeifen::before {
content: "\e602";
}
.i-wode1::before {
content: "\e600";
}
.i-zuojiantou::before {
content: "\e6e1";
}
.i-guanbi::before {
content: "\e674";
}
.i-shaixuantubiaogaozhuanqu03::before {
content: "\e6c6";
}
.i-youjiantou::before {
content: "\e62b";
}
.i-shouye::before {
content: "\e60d";
}
.i-diannao2::before {
content: "\e61e";
}
.i-sousuo::before {
content: "\e645";
}
.i-caidan::before {
content: "\e601";
}
@import './iconfont.wxss';
page {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
font-size: 28rpx;
color: #030303;
background-color: transparent;
}
iHeader组件
好了可以开始开发iheader数据组件了,再组件下创建iHeader组件,首先引入iAppbar头部布局组件
{
"component": true,
"usingComponents": {
"i-appbar":"../iAppbar"
}
}
const app = getApp()
const wininfo = app.globalData.windowInfo
/**
* 公共头部appbat
* @param { Number} types 显示类型 0 默认页面头部 1 tabBar页面头部
* @param { Number} screenType 屏幕类型 0 手机、1 平板、2 桌面
* @param { Boolean} screenType 是否显示切换设备类型的图标按钮
* @param { String} color 默认页面头部字体颜色
* @param { Number} backl 默认页面返回按钮边距(内边距)
* @param { Boolean} backi 默认页面是否显示手机切换图标
*/
Component({
options: {
addGlobalClass: true // 全局样式
},
behaviors: [],
properties: {
types: {
type: Number,
value: 1
},
screenType: {
type: Number,
value: 0
},
showSType: {
type: Boolean,
value: true
},
color: {
type: String,
value: 'black'
},
backl: {
type: Number,
value: 0
},
backi: {
type: Boolean,
value: false
}
},
observers: {
},
data: {
wininfo,
},
methods: {
// 菜单事件
onMenu(e) {
this.triggerEvent('menu', e)
},
// 屏幕类型事件
onClientType() {
// 短时间振动 [heavy:重、medium:中、 light:轻]
wx.vibrateShort({ type: 'medium' })
// 0:手机、1:平板、2:PC
let currentScreenType = this.data.screenType + 1
if (currentScreenType > 2) currentScreenType = 0
// 当前选择的显示模式保存本地
wx.setStorageSync('screen', currentScreenType)
// 更新全局数据
app.globalData.screenType = currentScreenType
// 触发事件
this.triggerEvent('sctype', currentScreenType)
},
// 返回
onBack() {
wx.navigateBack()
}
}
})
<wxs module="filter" src="./filter.wxs" />
<i-appbar>
<view slot="left" class="left">
<block wx:if="{{types === 1}}">
<view class="item" style="line-height: {{wininfo.clientRect.height}}px;" catch:tap="onMenu">
<text class="iconfont i-caidan text-{{skin}}" style="font-size: 18px"></text>
</view>
<view class="item" style="line-height: {{wininfo.clientRect.height}}px; justify-content: center;" catch:tap="onClientType">
<block wx:if="{{showSType}}">
<text class="iconfont i-{{filter.screen_type(screenType)}} text-{{skin}}" style="font-size: {{filter.screen_size(screenType)}}px"></text>
</block>
</view>
</block>
<view wx:else class="back-default" catch:tap="onBack" style="padding-left: {{backl}}px;width: 100%; height: 100%">
<text class="iconfont i-a-chexiaofanhui" style="color:{{color}}"></text>
<view wx:if="{{backi}}" class="item" style="line-height: {{wininfo.clientRect.height}}px; margin-left: 20px; text-align: right" catch:tap="onClientType">
<block wx:if="{{showSType}}">
<text class="iconfont i-{{filter.screen_type(screenType)}}" style="font-size: {{filter.screen_size(screenType)}}px; color: {{color}}"></text>
</block>
</view>
</view>
</view>
<view class="content">
<slot></slot>
</view>
</i-appbar>
.left {
width: 100%;
height: 100%;
display: flex;
align-items: center;
}
.left>.item {
width: 50%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
}
.menu {
background-color: rgba(255, 255, 255, 0.692);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
backdrop-filter: blur(10rpx);
}
.left>.item>text {
font-size: 34rpx;
font-weight: 500;
}
.text-light {
color: black;
}
.text-dark {
color: #f0f0f0;
}
.back-default {
width: 50%;
height: 100%;
display: flex;
align-items: center;
box-sizing: border-box;
}
.back-default>text {
font-size: 24px;
transform-origin: center center;
box-sizing: border-box;
}
.content {
width: 100%;
height: 100%;
display: flex;
align-items: center;
overflow: hidden;
box-sizing: border-box;
}
需要注意的是 .wxs 只能用es5 写 不支持 es5 以上语法。
var filter = {
screen_type: function (type) {
return type === 0 ? 'mobile' : type === 1 ? 'tablet' : 'diannao2'
},
screen_size: function (type) {
return type === 0 ? 21 : type === 1 ? 20 : 20
}
}
module.exports = {
screen_type: filter.screen_type,
screen_size: filter.screen_size
}
使用示例-1
{
"component": true,
"usingComponents": {
"i-header":"../../components/iHeader"
}
}
<view style="width: 100vw; height: 100vh; background-color: rgba(255, 0, 0, 0.2);">
<i-header></i-header>
</view>
使用示例-2
<view style="width: 100vw; height: 100vh; background-color: rgba(0, 255, 255, 0.5);">
<i-header showSType="{{false}}"></i-header>
</view>
使用示例-3
<view style="width: 100vw; height: 100vh; background-color: rgba(255, 0, 0, 0.7);">
<view style="width: 100%; background-color: white;">
<i-header types="{{0}}">
<view style="width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;"><text>个人中心</text></view>
</i-header>
</view>
</view>
iHeader 组件