предисловие
Некоторое время назад я узнал о разработке мини-программ WeChat, но я не практиковал поддельные трюки, поэтому планировал создать мини-программу WeChat самостоятельно. Я выбрала мини-программу стратегии путешествия Приходи на тренировку. Первый раз пишу небольшую программу и статью.Прошу простить за неадекватности,спасибо. Ниже я поделюсь проблемами, с которыми столкнулся, и опытом, полученным при написании небольших программ, надеюсь, это поможет вам, и вы можете меня поправить. Наконец, я хотел бы поблагодарить учителей и одноклассников, которые помогали мне, когда я писал апплет, а также всех известных и анонимных авторов Baidu, которые помогали мне. Я закончил говорить ерунду, сначала о рендерах проекта.
Подготовка перед разработкой
Все страницы проекта
Настройте компонент верхней панели навигации
Апплет WeChat поставляется сверхняя панель навигацииЯ не могу удовлетворить реальные потребности, поэтому я написал компонент сам.Если вы хотите использовать верхнюю панель навигациипользовательский компонент(Использование пользовательских компонентов подробно описано по ссылке) Свойство окна в app.json должно быть установлено на:"window": {
"navigationBarTextStyle": "black",//导航栏标题颜色,仅支持 black / white
"navigationStyle": "custom" //导航栏样式,仅支持以下值:default 默认样式custom 自定义导航栏,只保留右上角胶囊按钮
}
wxml
<view class='nav-wrap' style='height: {{height*2 + 20}}px; background-color:{{navbarData.backgroundColor}};opacity:{{navbarData.opacity}}'>
<view style="width:100%;height:100%;">
<!--城市名-->
<navigator url="/pages/destination/destination" hover-class="none">
<view class="nav-city" style='margin-top:{{height*2 + 20-36}}px;' wx:if='{{navbarData.showMain}}'>
<text>{{navbarData.cityName}}</text>
<view class="downtips"></view>
</view>
</navigator>
<navigator url="/pages/search/search" hover-class="none">
<!--搜索框-->
<view class="section" style='top:{{height*2 + 20-34}}px;' wx:if='{{navbarData.showMain}}'>
// 这里的搜索框不是一个input组件,只是一个view可供点击然后跳到搜索页
<view class='search_icon'>
<icon type='search' size='14px'></icon>
</view>
<view class='placehold'>搜索目的地/景点/攻略</view>
</view>
</navigator>
</view>
<!-- 标题 -->
<view wx:if="{{navbarData.title!=''}}" class='nav-title' style='line-height: {{height*2 + 44}}px;'>
{{navbarData.title}}
</view>
<!-- 返回上一级按钮 和 返回主页按钮-->
<block wx:if="{{navbarData.showCapsule===1}}">
<view class='nav'>
<view class='nav_back' bindtap="_navback">
<image src='/images/back.png'></image>
</view>
<view class="line"></view>
<view class='nav_home' bindtap="_backhome">
<image src='/images/home.png'></image>
</view>
</view>
</block>
</view>
Элементы в компоненте могут отображаться или нет через данные, переданные в компонент с текущей страницы.
js только что написал двапрыжок по маршрутуФункция, официальная документация апплета WeChat имеет очень подробное введение, поэтому я не буду здесь вдаваться в подробности.
интерфейс входа
При первом входе в апплет он перейдет на страницу авторизации входа, потому что апплет WeChat больше не поддерживается.wx.getUserInfo В интерфейсе напрямую всплывает метод разработки окна авторизации, поэтому здесь используется компонент-кнопка, а в качестве типа getUserInfo указывается open-type для получения основной информации о пользователе.<button style='background:green; color:#fff' open-type="getUserInfo" bindgetuserinfo="bindGetUserInfo">同意授权</button
После того, как апплет авторизует доступ к информации о пользователе, появится окно авторизации местоположения, чтобы получить текущее местоположение пользователя для отображения данных на главной странице. Вызов интерфейса, предоставленного апплетомwx.getLocation(Требуется авторизация пользователя) для получения широты и долготы, а затем использовать полученные широту и долготуBaidu Map Открытая платформаAPI, предоставленный аплету для получения названия текущего города и помещения названия города в кеш, чтобы его могла получить главная страница.
##Примечание. Использование wx.getLocation() необходимо настроить в app.json.
"permission": {
"scope.userLocation": {
"desc": "小程序将获取你的位置信息"
}
}
интерфейс входа js
// miniprogram/pages/login/login.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
show: false,
// 顶部导航栏数据
navbarData: {
showCapsule: 0, //是否显示左上角图标 1表示显示 0表示不显示
title: '马蜂窝旅游', //导航栏 中间的标题
backgroundColor: '#354a98', //'#354a98'
opacity: 1,
showMain: 0,
},
// 此页面 页面内容距最顶部的距离
height: app.globalData.height * 2 + 20,
},
bindGetUserInfo(res) {
let that =this
let info = res;
if (info.detail.userInfo) {
wx.login({
success: function (res) {
that.getPlaceData()
}
})
}
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
let that = this;
//页面加载时判断用户是否授权过,如果授权过直接跳到主页面,没有就显示授权按钮
wx.getUserInfo({
success: function (res) {
wx.switchTab({
url: '/pages/main/index'
})
},
fail(err) {
that.setData({
show: true
})
}
})
},
// 获取城市名字
getCityName(location) {
return new Promise((resolve, reject) => {
let that = this;
var e = {
coord_type: "gcj02",
output: "json",
pois: 0,
ak: '',//放上自己的ak密钥 密钥申请见上文百度地图开方平台链接
sn: "",
timestamp: ""
};
e.location = location;
wx.request({
url: "https://api.map.baidu.com/geocoder/v2/",
data: e,
header: {
"content-type": "application/json"
},
method: "GET",
success: function (t) {
let currentCity = t.data.result.addressComponent.city;
if (currentCity.slice(currentCity.length - 1) == "市") {
currentCity = currentCity.slice(0, currentCity.length - 1)
}
wx.setStorageSync('currentCity', currentCity)
resolve(currentCity) //通过城市名字 请求城市数据
}
})
})
},
// 获取经纬度
getLocation() {
return new Promise((resolve, reject) => {
wx.getLocation({
type: 'wgs84',
success(res) {
const latitude = res.latitude
const longitude = res.longitude
let location = latitude + ',' + longitude
console.log(location)
resolve(location) //获取城市名字
}
})
})
},
getPlaceData() { // 获取地理信息
let that = this
this.getLocation().then((val) => {
return that.getCityName(val)
}).then(()=>{
wx.switchTab({
url: '/pages/main/index'
})
})
}
})
Главная страница
Когда я писал апплет, я не знал, что главная страница имеет два стиля, когда я это знал, я уже много чего написал, поэтому я не писал его как компонент, и код выглядел очень многословно. .Это моя ошибка(мангфу) ,надеюсь что когда будете думать какой апплет написать,вы должны подумать о структуре страниц апплета,иначе будет как у меня.Если хотите изменить то будете нужно много места поменять.
- Общая страница города
- Страницы популярных городов
При входе на домашнюю страницу страница сначала получит название города в кеше, затем запросит данные через название города, а затем по атрибуту ishot в запрашиваемых данных, если атрибут ishot равен true, страница популярного будет отображаться город, в противном случае будет отображаться обычная страница города
'Моя страница
Страница «Моя» в основном используется для отображения содержимого избранного пользователя.Страница сведений о аттракционе
По разным причинам (lan) большая часть данных на странице не помещается в Easy Mock.Mafengwo славится своими большими данными, а ttm данных много.
Страница со списком континентов/стран/городов
Макет этой страницы разделен на три части: окно поиска в заголовке определяется абсолютным позиционированием, список континентов слева определяется абсолютным позиционированием, а страны на континентах справа являются компонентами, которые поставляются с апплет WeChat.scroll-view
wxml
<!-- pages/destination/destination.wxml -->
<nav-bar navbar-data='{{navbarData}}'></nav-bar>
<view class="destination" style='top: {{height}}px'>
<!--头部-->
<view class="des_head">
<navigator url="/pages/search/search" hover-class="none">
<view class="des_search">
<view class="des_search_icon">
<icon type='search' size='30rpx' color="#000000"></icon>
</view>
搜索目的地
</view>
</navigator>
</view>
<!--左部-->
<view class="des_continents">
<view class="des_continent {{curIndex===index?'add':''}}}" wx:for="{{continents}}" wx:for-item="continent" wx:key='{{index}}' data-index='{{index}}' bindtap="switch_des">
<view class='des_continent_name {{curIndex===index?"on":""}}}'>{{continent.name}}</view>
</view>
</view>
<!--右部-->
<scroll-view class='des_cities' scroll-y>
<block wx:if="{{curIndex==0}}">
<view class="des_cities_content" wx:for="{{continents[curIndex].cities}}" wx:key="{{index}}" wx:for-item="des_city">
<view class="des_cities_title">{{des_city.title}}</view>
<view class="des_city" wx:for="{{des_city.city}}" wx:key="{{index}}" bindtap='goMain' data-city_name="{{item.city_name}}">
{{item.city_name}}
</view>
</view>
</block>
<block wx:else>
<view class="des_area" wx:for="{{continents[curIndex].cities}}" wx:key="{{index}}" wx:for-item="des_city" bindtap='goMain' data-city_name="{{des_city.city_name}}">
<view class="des_img">
<image src="{{des_city.img}}" />
</view>
<view class="des_city_name">{{des_city.city_name}}</view>
</view>
</block>
</scroll-view>
</view>
js
// pages/destination/destination.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
<!--顶部导航栏数据-->
navbarData: {
showCapsule: 1, //是否显示左上角图标 1表示显示 0表示不显示
title: '目的地切换', //导航栏 中间的标题
backgroundColor: '#fff',//背景颜色
showMain: 0 ///显示搜索框
},
height: app.globalData.height * 2 + 20,
continents: [],
curIndex: 0 //当前洲的索引值
},
<!--左部各大洲的点击事件,来改变右边显示的内容,并且改变自身样式-->
switch_des(e) {
let curIndex = e.currentTarget.dataset.index;
this.setData({
curIndex,
})
},
<!--右部国家/城市的点击事件,获取点击的元素上绑定的国家/城市的名字,放入缓存,并跳转到主页-->
goMain(e){
const city_name = e.currentTarget.dataset.city_name;
wx.setStorageSync('currentCity', city_name)
wx.switchTab({
url: '/pages/main/index'
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
let that = this
<!--请求数据-->
wx.request({
url: 'https://www.easy-mock.com/mock/5ca457f04767c3737055c868/example/mafengwo/continents',
success:(res)=>{
that.setData({
continents: res.data.continents
})
}
})
}
}
страница поиска
Реализованный функционал
Нажмите, чтобы переключить список
Возьмите домашнюю страницу в качестве примера
На самом деле все списки переключателей имеют схожие функции, для этого нужно установить кнопку на выбранный элемент.пользовательские свойства(Data- *) — это уникальное значение индекса, сbind-tapПривяжите событие клика, получите уникальное значение индекса через событие клика, затем перейдите к источнику данных, чтобы найти желаемое содержимое с помощью уникального значения индекса, а затем управляйте содержимым, отображаемым на странице, с помощью данных и задайте такие данные как mcurIndex в источнике данных данных, указывает текущий выбранный элемент, который используется, чтобы отличать его от других элементов и отображать разные стили.
wxml
<view class='menu_list'>
<!-- {{mcurIndex===index?"on":""}} 表示如果自身的索引值为当前选择的元素索引值时,添加一个类名‘on’-->
<view class='list {{mcurIndex===index?"on":""}}' wx:for="{{placeData.allGuide}}" data-mindex="{{index}}" bindtap='selected_menu' wx:key="{{index}}">
{{item.name}}
</view>
</view>
js
selected_menu(e) {
this.setData({
mcurIndex: e.target.dataset.mindex,
size: 0,
showend: false
})
<!--调用自己写的函数来获取要显示的内容的数据-->
this.bitiyan()
}
Сдвиньте страницу, чтобы изменить видимость и всплывающую загрузку верхней панели навигации.
-
Проведите по странице, чтобы изменить видимость верхней панели навигации.
Возьмите домашнюю страницу в качестве примера
<scroll-view class="main_scro" scroll-y bindscroll="scroll" bindscrolltolower="bindDownLoad">
</scroll-view>
js
scroll(e) {
let opacity = 0;
if (e.detail.scrollTop < 60) {
opacity = (e.detail.scrollTop / 100).toFixed(1);
} else {
opacity = 1;
}
this.data.navbarData.opacity = opacity;
if (e.detail.scrollTop<10){
this.setData({
shownav: false
})
}else{
this.setData({
shownav: true
})
}
this.setData({
navbarData: this.data.navbarData,
})
}
-
подтягивающая загрузка
Возьмите домашнюю страницу в качестве примера
Метод реализации здесь вscroll-viewДобавление атрибута bindscrolltolower к компоненту вызовет событие, связанное с bindscrolltolower, когда страница достигнет нижней части.
<scroll-view class="main_scro" scroll-y bindscroll="scroll" bindscrolltolower="bindDownLoad">
</scroll-view>
bindDownLoad() {
let part = 0; //已经显示的数据长度
let all = 0; //总的数据长度
<!--判断当前城市是否为热门城市-->
if (this.data.ishot) {
// 待完善 因为效果相同就没写了
} else {
if (this.data.mcurIndex === 0) {
part = this.data.cur_view.length * 2;
all = this.data.placeData.allGuide[this.data.mcurIndex].content[this.data.hlcurIndex].content.length;
} else {
part = this.data.cur_view.length;
all = this.data.placeData.allGuide[this.data.mcurIndex].content.length;
}
if (part < all) {
wx.showLoading({
title: '正在加载'
})
setTimeout(() => {
this.bitiyan(this.data.placeData)
wx.hideLoading()
}, 1000)
} else {
<!--当所有数据都加载完了,就显示end 图标-->
this.setData({
showend: true
})
}
}
}
Несколько замечаний о компоненте scroll-view
- При настройке вертикальной прокрутки вы должны установить высоту.Иногда вы обнаружите, что после установки высоты на 100%, когда вы скользите вниз, отображение будет неполным.В настоящее время это зависит от того, установили ли вы поля/ заполнение или родитель Элемент установлен с полем/отступом.В это время высота компонента прокрутки должна быть вычтена из соответствующего поля/отступа.
- При настройке горизонтальной прокрутки следует отметить, что элементы, которые необходимо перемещать в представлении прокрутки, не могут перемещаться с помощью float; большое поле в представлении прокрутки, которое оборачивает элементы, которые необходимо перемещать, использует display:flex неэффективно; представление прокрутки в элементах, которые необходимо скользить, должно быть расположено горизонтально с помощью display:inline-block; большое окно, обертывающее представление прокрутки, имеет четкую ширину и стиль --> overflow:hidden;white-space:nowrap ;
Любимая функция
Я написал функцию сбора в компоненте.Изначально я хотел использовать ее для нескольких страниц, как верхний компонент.Позднее, поскольку только одна из написанных мной страниц была полезна для этого компонента, я не буду здесь объяснять этот компонент отдельно, и это компонент Это в основном то же самое, что и верхний компонент.
Реализация функции сбора, когда щелчок по определенному живописному месту вызывает событие щелчка, я думаю, вы видели функцию переключения списка, вы уже знаетеbind-tapМетод использования здесь повторяться не будет. Вот чтобы получить пользовательский атрибут элемента,параметры маршрутизацииМетод передается на страницу сведений, и страница сведений получает соответствующие данные из источника данных в соответствии с переданными данными, а затем передает данные компоненту. будет запущено событие, а затем обновление данных избранного collectData в кеше. На странице «Моя» отображаются данные из избранного
Страница сведений js
<!--生命周期函数,监听页面加载-->
onLoad: function(options) {
<!--options中包含了传递过来的参数-->
let name = options.name;
this.getinfo(name)
},
<!--通过名字获取想要的数据-->
getinfo(name){
<!--先获取缓存中已经存在的收藏夹数据,如果不存在就将collectData设为空数组-->
let collectData = wx.getStorageSync('collectData') || [];
if (collectData.filter(e => e.name === name).length > 0) {
this.setData({
placeData: collectData.filter(e => e.name === name)[0]
})
} else {
let placeData = wx.getStorageSync('placeData')
let view = placeData.allGuide[0].content.map(e => e.content)
let newView = []
for (let i = 0; i < view.length; i++) {
newView.push(...view[i])
}
this.setData({
placeData: newView.find(e => e.name === name)
})
}
this.setBottom();
},
<!--设置要传递给bottom组件的数据-->
setBottom(){
this.data.bottomData.placeData = this.data.placeData;
let bottomData = this.data.bottomData;
this.setData({
bottomData
})
}
js для нижнего компонента
// components/bottom/bottom.js
const app = getApp()
Component({
/**
* 组件的属性列表
*/
properties: {
bottomData: { // 由父页面传递的数据,变量名字自命名
type: Object,
value: {},
observer: function (newVal, oldVal) {
}
}
},
/**
* 组件的初始数据
*/
data: {
height: ''
},
attached: function () {
// 获取是否是通过分享进入的小程序
this.setData({
share: app.globalData.share
})
// 定义导航栏的高度 方便对齐
this.setData({
height: app.globalData.height
})
},
/**
* 组件的方法列表
*/
methods: {
<!--点击收藏按钮触发的事件-->
collected(){
<!--将isCollect(是否收藏过),collectors(收藏人数)从数据中解构出来-->
let {isCollect,collectors} = this.data.bottomData.placeData;
isCollect = !isCollect;
this.data.bottomData.placeData.isCollect = isCollect;
let collectData = wx.getStorageSync('collectData') || [];
if(isCollect){
wx.showToast({
title: '收藏成功',
icon: 'success',
duration: 2000
})
collectors++;
collectData.push(this.data.bottomData.placeData);
}else{
wx.showToast({
title: '已取消收藏',
icon: 'success',
duration: 2000
})
collectors--;
collectData = collectData.filter(e => e.name != this.data.bottomData.placeData.name)
}
this.data.bottomData.placeData.collectors = collectors;
<!--将收藏夹数据放入缓存-->
wx.setStorageSync('collectData', collectData)
let bottomData = this.data.bottomData;
this.setData({
bottomData
})
}
}
})
Функция поиска
эффект один
эффект дваФункция поиска реализована через нативные компонентыinputАтрибут bindinput в приведенном выше примере запускает метод привязки атрибута bindinput при вводе с клавиатуры, получает введенное значение в режиме реального времени, затем помещает полученное значение в адрес запроса для запроса данных, а затем помещает запрошенные данные в данные. данные страницы В источнике, когда запрошенные данные не пусты, все соответствующие полученные данные будут отображаться на странице, например эффект 1. Срабатывает при нажатии кнопки поискаinputСобытие, связанное атрибутом bindconfirm на поле, первый элемент запрошенных данных будет отображаться на странице, например эффект два.
wxml
<input style='width:500rpx' bindconfirm='confirm' confirm-type='search' focus='true' placeholder="搜索目的地/景点/攻略" bindinput='search'></input>
js
// pages/search/search.js
const app = getApp()
Page({
/**
* 页面的初始数据
*/
data: {
navbarData: {
showCapsule: 1, //是否显示左上角图标 1表示显示 0表示不显示
title: '马蜂窝旅游', //导航栏 中间的标题
backgroundColor: '#ffffff', //'#354a98'
city: '',
opacity: 1,
showMain: 0
},
height: app.globalData.height * 2 + 20,
result: [],
searchparams: '',
show: true,
searchHistory: [],
showResult: false,
showconfirm: false,
placedata: []
},
<!--清空历史纪录-->
clear() {
this.setData({
searchHistory: []
})
wx.removeStorageSync('searchHistory')
},
<!--当点击键盘上搜索按钮触发的事件-->
confirm(e) {
if (e.detail.value != '') {
let searchHistory = wx.getStorageSync('searchHistory') || []
if (searchHistory.filter(a => a === e.detail.value).length === 0) {
searchHistory.push(e.detail.value)
wx.setStorageSync('searchHistory', searchHistory)
}
if (this.data.result.length > 0) {
let currentCity = this.data.result[0].name;
this.getCityDataByName(currentCity);
}
this.setData({
show: false,
showResult: false,
showconfirm: true
})
}
},
<!--跳到主页面-->
gotomain(e) {
wx.setStorageSync('currentCity', e.currentTarget.dataset.name)
wx.switchTab({
url: '/pages/main/index',
})
},
<!--点击历史纪录触发的事件,效果和confirm方法基本相同,不同的是confirm是从页面data中获取数据,而dosearch是从接口中获取数据-->
gosearch(e) {
let that = this
wx.request({
url: `https://www.easy-mock.com/mock/5ca457f04767c3737055c868/example/mafengwo/search?name=${e.currentTarget.dataset.name}`,
success: (res) => {
if (res.data.data.length > 0) {
that.getCityDataByName(res.data.data[0].name)
} else {
this.setData({
show: false,
showResult: false,
showconfirm: true
})
}
}
})
},
// 通过城市名字 获取城市数据
getCityDataByName(cityname) {
let that = this
wx.request({
url: 'https://www.easy-mock.com/mock/5ca457f04767c3737055c868/example/mafengwo/china',
success: (res) => {
let placedata = [];
placedata.push(...res.data.data.china.filter(e => e.chName === cityname))
that.setData({
placedata,
show: false,
showResult: false,
showconfirm: true
})
}
})
},
<!--当键盘输入时触发的事件-->
search(e) {
let that = this
wx.request({
url: `https://www.easy-mock.com/mock/5ca457f04767c3737055c868/example/mafengwo/search?name=${e.detail.value}`,
success: (res) => {
if (res.data.data.length > 0) {
that.changecolor(res.data.data, e.detail.value)
} else {
that.setData({
result: [],
searchparams: '',
showResult: false
})
}
}
})
},
<!--改变名字颜色-->
changecolor(result, searchparams) {
for (let j = 0; j < result.length; j++) {
let i = result[j].name.search(searchparams);
let left = result[j].name.slice(0, i),
mid = result[j].name.slice(i, i + searchparams.length),
right = result[j].name.slice(i + searchparams.length);
result[j].left = left;
result[j].mid = mid;
result[j].right = right;
}
this.setData({
result,
searchparams,
show: false,
showResult: true,
showconfirm: false
})
},
_navback() {
wx.navigateBack({
delta: 1
})
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function() {
<!--获取缓存中的搜索历史并放入数据源-->
let searchHistory = wx.getStorageSync('searchHistory') || []
this.setData({
searchHistory
})
}
Этот интерфейс API - это то, что я используюEasy Mockнаписано
Ссылка на простой фиктивный адрес
Легкий фиктивный код
{
"data": function({
_req
}) {
let i = 0,
<!--数据源_data由于篇幅原因就放了一小段数据-->
_data = [
{
name: '亚洲',
type: '目的地'
},
{
name: '欧洲',
type: '目的地'
},
{
name: '大洋洲',
type: '目的地'
},
{
name: '非洲',
type: '目的地'
},
{
name: '北美洲',
type: '目的地'
},
{
name: '南美洲',
type: '目的地'
},
{
name: '南极洲',
type: '目的地'
}
],
<!--_req是easymock封装的对象,_req.query(将查询参数字符串进行解析并以对象的形式返回,如果没有查询参数字字符串则返回一个空对象);-->
name = _req.query.name;
if (name != '') {
<!--当输入的值不为空时-->
let result = [];
let data = []
for (let j = 0; j < result.length; j++) {
<!--eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。这里主要是为了给正则表达式动态传参-->
if (eval('/' + name + '/').test(result[j].name)) {
data.push(result[j])
}
<!--当查询到8个匹配项时跳出循环-->
if (data.length > 8) break;
}
return data
} else {
<!--当输入的值为空时直接返回空数组-->
return []
}
}
}
Популярные городские анимации
Поскольку анимация имеет только 6 элементов, нет необходимости записывать ее в виде массива для обхода и создания, просто напишите 6 блоков, инициализируйте их стили и дайте им вернуться в исходное положение. Апплет WeChat позволяет создавать экземпляры анимации.API wx.createAnimation
wxml
<view class='video a' animation="{{animation1}}" data-index='0' bindtap="_play">
<view class='context'>
<text>{{placeData.vlog[0].title}}</text>
</view>
<view class='vdoIcon'>
<image src='/images/play.png'></image>
</view>
</view>
<view class='video b' animation="{{animation2}}" data-index='1' bindtap="_play">
<view class='context'>
<text>{{placeData.vlog[1].title}}</text>
</view>
<view class='vdoIcon'>
<image src='/images/play.png'></image>
</view>
</view>
<view class='video c' animation="{{animation3}}" data-index='2' bindtap="_play">
<view class='context'>
<text>{{placeData.vlog[2].title}}</text>
</view>
<view class='vdoIcon'>
<image src='/images/play.png'></image>
</view>
</view>
<view class='video d' animation="{{animation4}}" data-index='3' bindtap="_play">
<view class='context'>
<text>{{placeData.vlog[3].title}}</text>
</view>
<view class='vdoIcon'>
<image src='/images/play.png'></image>
</view>
</view>
<view class='video e' animation="{{animation5}}" data-index='4' bindtap="_play">
<view class='context'>
<text>{{placeData.vlog[4].title}}</text>
</view>
<view class='vdoIcon'>
<image src='/images/play.png'></image>
</view>
</view>
<view class='video f' animation="{{animation6}}" data-index='5' bindtap="_play">
<view class='context'>
<text>{{placeData.vlog[5].title}}</text>
</view>
<view class='vdoIcon'>
<image src='/images/play.png'></image>
</view>
</view>
wxss
.a{
opacity: 0.9;
}
.b{
transform: translate(170rpx,-110rpx) scale(0.8);
opacity: 0.8;
}
.c{
transform: translate(210rpx,-250rpx) scale(0.7);
opacity: 0.7;
}
.d{
transform: translate(10rpx,-350rpx) scale(0.6);
opacity: 0.6;
}
.e{
transform: translate(-250rpx,-290rpx) scale(0.8);
opacity: 0.5;
}
.f{
transform: translate(-300rpx,-130rpx) scale(0.9);
opacity: 0.8;
}
js
// 动画的运行路线
translate: function(i) {
// 获取屏幕宽度来实现自适应
let windowwidth = this.data.windowWidth;
//动画的运行状态status[x轴偏移量,y轴偏移量,scale缩放倍数,opacity透明度],也是动画的运行路线
let status = [
[170, -110, 0.8, 0.7],
[210, -250, 0.7, 0.6],
[10, -350, 0.6, 0.5],
[-250, -300, 0.8, 0.7],
[-300, -130, 0.9, 0.8],
[0, 0, 1, 0.9]
];
let x = 0,
y = 0,
scale = 0,
opacity = 0;
for (let j = 0; j < 6; j++) {
let animationName = 'animation' + (j + 1);
x = status[(i + j) % 6][0] / 750 * windowwidth;
y = status[(i + j) % 6][1] / 750 * windowwidth;
scale = status[(i + j) % 6][2];
opacity = status[(i + j) % 6][3];
this.animation.translate(x, y).scale(scale).opacity(opacity).step()
this.setData({
[animationName]: this.animation.export()//导出动画数据传递给组件的 animation 属性
})
}
},
hotCityAnimation() {
let i = 0;
<!--创建动画实例-->
this.animation = wx.createAnimation({
duration: 2000,
timingFunction: 'ease',
})
let that = this
let anicontrol = this.data.anicontrol
anicontrol = setInterval(function() {
that.translate(i)
if (i == 5) {
i = -1;
}
i++;
}, 3000)
this.setData({
anicontrol
})
}
Здесь следует отметить, что поскольку это анимация, написанная на странице вкладки, и используется таймер setinterval, зарегистрированная функция обратного вызова будет выполняться в соответствии с заданным циклом (в миллисекундах), а это означает, что даже если вы перейдете к другому страница, анимация все еще работает.Когда вы вернетесь на домашнюю страницу, анимация будет работать неправильно, и появятся призраки и животные. Поэтому в функции цикла onHide домашней страницы таймер сбрасывается при открытии страницы мониторинга. скрыт, и экземпляр анимации также очищается.
onHide: function() {
let anicontrol = this.data.anicontrol;
clearInterval(anicontrol)
this.setData({
animation1: '',
animation2: '',
animation3: '',
animation4: '',
animation5: '',
animation6: ''
})
}
О css
Я не использовал никакой UI-фреймворк для написания этого апплета. У этого есть как недостатки, так и преимущества. Недостаток в том, что код работает медленно, а преимущество в том, что я хорошо разобрался в CSS. Те, кто хочет использовать структуру пользовательского интерфейса, могут использоватьWeUI. По ссылке есть подробная информация о том, как его использовать.
Эпилог
Из-за времени и сил апплет написал всего несколько страниц и малую часть функций.В процессе написания проекта я также обнаружил много недочетов, поэтому много мучился, но и многому научился Можно сказать, что Боль и счастье. Я надеюсь, что эта статья может быть полезна для тех, кто планирует писать небольшие программы.Исходный код на гитхабеЗдесь требуется самовывоз.