Skip to content
体验新版
项目
组织
正在加载...
登录
切换导航
打开侧边栏
DCloud
hello uni-app x
提交
15096a0a
H
hello uni-app x
项目概览
DCloud
/
hello uni-app x
通知
6424
Star
114
Fork
190
代码
文件
提交
分支
Tags
贡献者
分支图
Diff
Issue
25
列表
看板
标记
里程碑
合并请求
2
DevOps
流水线
流水线任务
计划
Wiki
0
Wiki
分析
仓库
DevOps
项目成员
Pages
H
hello uni-app x
项目概览
项目概览
详情
发布
仓库
仓库
文件
提交
分支
标签
贡献者
分支图
比较
Issue
25
Issue
25
列表
看板
标记
里程碑
合并请求
2
合并请求
2
Pages
DevOps
DevOps
流水线
流水线任务
计划
分析
分析
仓库分析
DevOps
Wiki
0
Wiki
成员
成员
收起侧边栏
关闭侧边栏
动态
分支图
创建新Issue
流水线任务
提交
Issue看板
提交
15096a0a
编写于
12月 07, 2023
作者:
雪洛
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
fix: 修正部分错误用法,如map应使用get/set访问设置属性,this指向问题,type声明属性默认值(不可作为特性使用)
上级
2774843b
变更
23
展开全部
隐藏空白更改
内联
并排
Showing
23 changed file
with
2990 addition
and
2977 deletion
+2990
-2977
components/uni-collapse-item/util.uts
components/uni-collapse-item/util.uts
+18
-18
components/uni-collapse/uni-collapse.vue
components/uni-collapse/uni-collapse.vue
+1
-1
pages.json
pages.json
+943
-941
pages/API/element-draw/element-draw.uvue
pages/API/element-draw/element-draw.uvue
+2
-2
pages/API/element-takesnapshot/element-takesnapshot.uvue
pages/API/element-takesnapshot/element-takesnapshot.uvue
+2
-2
pages/API/facial-recognition-verify/facial-recognition-verify.uvue
.../facial-recognition-verify/facial-recognition-verify.uvue
+5
-5
pages/API/unicloud-import-object/unicloud-import-object.uvue
pages/API/unicloud-import-object/unicloud-import-object.uvue
+1
-1
pages/component/list-view/list-view-multiplex.uvue
pages/component/list-view/list-view-multiplex.uvue
+1
-1
pages/component/list-view/list-view.uvue
pages/component/list-view/list-view.uvue
+14
-14
pages/component/scroll-view/scroll-view-refresher.uvue
pages/component/scroll-view/scroll-view-refresher.uvue
+93
-93
pages/component/sticky-header/sticky-header.uvue
pages/component/sticky-header/sticky-header.uvue
+53
-53
pages/component/unicloud-db-contacts/detail.uvue
pages/component/unicloud-db-contacts/detail.uvue
+1
-1
pages/component/unicloud-db-contacts/edit.uvue
pages/component/unicloud-db-contacts/edit.uvue
+2
-2
pages/component/view/view.uvue
pages/component/view/view.uvue
+77
-77
pages/tabBar/component.uvue
pages/tabBar/component.uvue
+317
-315
pages/template/calendar/calendar.uvue
pages/template/calendar/calendar.uvue
+291
-291
pages/template/custom-tab-bar/custom-tab-bar.uvue
pages/template/custom-tab-bar/custom-tab-bar.uvue
+4
-2
pages/template/drop-card/card/card.uvue
pages/template/drop-card/card/card.uvue
+243
-242
pages/template/list-news/detail/detail.uvue
pages/template/list-news/detail/detail.uvue
+1
-1
pages/template/long-list/long-list.uvue
pages/template/long-list/long-list.uvue
+268
-265
pages/template/long-list2/long-list2.uvue
pages/template/long-list2/long-list2.uvue
+257
-254
pages/template/pull-zoom-image/pull-zoom-image.uvue
pages/template/pull-zoom-image/pull-zoom-image.uvue
+120
-120
pages/template/swiper-vertical-video/swiper-vertical-video.uvue
...template/swiper-vertical-video/swiper-vertical-video.uvue
+276
-276
未找到文件。
components/uni-collapse-item/util.uts
浏览文件 @
15096a0a
// 查找父组件实例
export function $dispatch(
context : ComponentPublicInstance,
componentName : string,
eventName : string,
...params : any
| null
) {
let parent = context.$parent
let name = parent?.$options?.name
while (parent != null && (name == null || componentName != name)) {
parent = parent.$parent
if (parent != null) {
name = parent.$options.name
}
}
if (parent != null) {
parent.$callMethod(eventName, ...params)
}
// 查找父组件实例
export function $dispatch(
context : ComponentPublicInstance,
componentName : string,
eventName : string,
...params : any
[]
) {
let parent = context.$parent
let name = parent?.$options?.name
while (parent != null && (name == null || componentName != name)) {
parent = parent.$parent
if (parent != null) {
name = parent.$options.name
}
}
if (parent != null) {
parent.$callMethod(eventName, ...params)
}
}
components/uni-collapse/uni-collapse.vue
浏览文件 @
15096a0a
...
...
@@ -17,7 +17,7 @@
},
data
()
{
return
{
child_nodes
:
[]
as
Array
<
ComponentPublicInstance
>
child_nodes
:
[]
as
Array
<
ComponentPublicInstance
>
};
},
...
...
pages.json
浏览文件 @
15096a0a
此差异已折叠。
点击以展开。
pages/API/element-draw/element-draw.uvue
浏览文件 @
15096a0a
...
...
@@ -216,9 +216,9 @@
let hexChars = "0123456789ABCDEF";
let result = "";
while (num > 0) {
let remainder
: Int = num.toInt(
) % 16;
let remainder
= Math.floor(num
) % 16;
result = hexChars[remainder] + result;
num = Math.floor(
num.toInt(
) / 16);
num = Math.floor(
Math.floor(num
) / 16);
}
if (result.length == 1) {
return "0" + result
...
...
pages/API/element-takesnapshot/element-takesnapshot.uvue
浏览文件 @
15096a0a
...
...
@@ -23,7 +23,7 @@
takeSnapshotClick() {
const view = uni.getElementById('snapshot-content')!
view.takeSnapshot({
success:
function (res)
{
success:
(res) =>
{
console.log(res.tempFilePath)
this.snapImage = res.tempFilePath
this.mode = 'widthFix'
...
...
@@ -32,7 +32,7 @@
icon: "none"
})
},
fail:
function (res)
{
fail:
(res) =>
{
console.log(res)
uni.showToast({
icon: 'error',
...
...
pages/API/facial-recognition-verify/facial-recognition-verify.uvue
浏览文件 @
15096a0a
...
...
@@ -51,7 +51,7 @@
try {
metaInfo = uni.getFacialRecognitionMetaInfo();
} catch (e) {
if (
e
.message!.indexOf('Failed resolution of') > -1) {
if (
(e as Error)
.message!.indexOf('Failed resolution of') > -1) {
uni.showModal({
title: '提示',
content: '注意:目前uni-app x内使用实人认证功能需要打包自定义基座。',
...
...
@@ -65,7 +65,7 @@
idCard,
metaInfo
})
.then
<string>
((res : UTSJSONObject) : Promise<string> => {
.then((res : UTSJSONObject) : Promise<string> => {
const certifyId = res['certifyId'] as string
return new Promise((
resolve : (res : string) => void,
...
...
@@ -82,13 +82,13 @@
})
})
})
.then
<UTSJSONObject>
((certifyId : string) : Promise<UTSJSONObject> => {
.then((certifyId : string) : Promise<UTSJSONObject> => {
return testFacialCo.getAuthResult(certifyId)
})
.then
<void>
((res : UTSJSONObject) => {
.then((res : UTSJSONObject) => {
console.log('res', res)
})
.catch
<void>
((err : any | null) => {
.catch((err : any | null) => {
console.error('error', err)
})
}
...
...
pages/API/unicloud-import-object/unicloud-import-object.uvue
浏览文件 @
15096a0a
...
...
@@ -98,7 +98,7 @@
console.error(error)
if (this.isUniTest) {
uni.showToast({
title: err.errMsg
title: err
or
.errMsg
})
}
})
...
...
pages/component/list-view/list-view-multiplex.uvue
浏览文件 @
15096a0a
...
...
@@ -19,7 +19,7 @@
},
methods: {
onScrollTolower(_: ScrollToLowerEvent) {
setTimeout(
function()
{
setTimeout(
() =>
{
this.item_count += 20
}, 300)
},
...
...
pages/component/list-view/list-view.uvue
浏览文件 @
15096a0a
...
...
@@ -56,7 +56,7 @@
this.refresher_triggered_boolean = true
this.state = 2
this.reset = false;
setTimeout(
function()
{
setTimeout(
() =>
{
this.refresher_triggered_boolean = false
}, 1500)
},
...
...
@@ -89,24 +89,24 @@
confirm_scroll_top_input(value : number) { this.scroll_top_input = value },
confirm_scroll_left_input(value : number) { this.scroll_left_input = value },
confirm_refresher_background_input(value : string) { this.refresher_background_input = value },
item_change_size_enum(index : number) { this.scrollIntoView = "item---"+index },
item_change_size_enum(index : number) { this.scrollIntoView = "item---"+index },
//自动化测试例专用
check_scroll_height(): Boolean {
check_scroll_height(): Boolean {
var listElement = this.$refs["listview"] as Element
console.log("check_scroll_height--"+listElement.scrollHeight)
if(listElement.scrollHeight > 2000) {
return true
}
return false
console.log("check_scroll_height--"+listElement.scrollHeight)
if(listElement.scrollHeight > 2000) {
return true
}
return false
},
//自动化测试例专用
check_scroll_width(): Boolean {
//自动化测试例专用
check_scroll_width(): Boolean {
var listElement = this.$refs["listview"] as Element
console.log("check_scroll_width"+listElement.scrollWidth)
if(listElement.scrollWidth > 2000) {
return true
}
return false
}
return false
},
change_refresher_style_boolean(checked : boolean) {
if(checked) {
...
...
@@ -170,7 +170,7 @@
@confirm="confirm_refresher_background_input"></input-data>
<enum-data :items="size_enum" title="通过id位置跳转" @change="item_change_size_enum"></enum-data>
<navigator url="/pages/component/list-view/list-view-multiplex" hover-class="none">
<button type="primary" class="button">
list-view 对list-item复用测试
...
...
@@ -212,7 +212,7 @@
height: 45px;
width: 100%;
}
.button {
margin-top: 30rpx;
}
...
...
pages/component/scroll-view/scroll-view-refresher.uvue
浏览文件 @
15096a0a
<template>
<view class="container">
<page-head title="scroll-view 下拉刷新"></page-head>
<scroll-view class="scroll" refresher-enabled = true :refresher-triggered = "refresherTriggered"
@refresherrefresh="onRefresherrefresh" @refresherabort="onRefresherabort" @refresherrestore="onRefresherrestore"
@refresherpulling="onRefresherpulling" @scrolltolower="onScrolltolower">
<view v-for="key in scrollData" :key="key">
<view class="scroll-item">
<text class="scroll-item-title">{{key}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
scrollData: [] as Array <string>,
<template>
<view class="container">
<page-head title="scroll-view 下拉刷新"></page-head>
<scroll-view class="scroll" refresher-enabled = true :refresher-triggered = "refresherTriggered"
@refresherrefresh="onRefresherrefresh" @refresherabort="onRefresherabort" @refresherrestore="onRefresherrestore"
@refresherpulling="onRefresherpulling" @scrolltolower="onScrolltolower">
<view v-for="key in scrollData" :key="key">
<view class="scroll-item">
<text class="scroll-item-title">{{key}}</text>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
export default {
data() {
return {
scrollData: [] as Array <string>,
refresherTriggered: false,
refresherrefresh: false
};
},
onLoad() {
let lists: Array < string > = []
for (let i = 0; i < 20; i++) {
lists.push("item---"+i)
}
this.scrollData = lists
},
methods: {
refresherrefresh: false
};
},
onLoad() {
let lists: Array < string > = []
for (let i = 0; i < 20; i++) {
lists.push("item---"+i)
}
this.scrollData = lists
},
methods: {
onRefresherrefresh(_: RefresherEvent) {
this.refresherrefresh = true
console.log("onRefresherrefresh--------------下拉刷新触发")
this.refresherTriggered = true
setTimeout(
function(){
this.refresherTriggered = false
}, 1500)
},
onRefresherabort(_: RefresherEvent) {
console.log("onRefresherabort------下拉刷新被中止")
},
this.refresherrefresh = true
console.log("onRefresherrefresh--------------下拉刷新触发")
this.refresherTriggered = true
setTimeout(
() => {
this.refresherTriggered = false
}, 1500)
},
onRefresherabort(_: RefresherEvent) {
console.log("onRefresherabort------下拉刷新被中止")
},
onRefresherrestore(_: RefresherEvent) {
this.refresherrefresh = false
console.log("onRefresherrestore------下拉刷新被复位")
},
onRefresherpulling(e: RefresherEvent) {
console.log("onRefresherrestore------拉刷新控件被下拉-dy="+e.detail.dy)
},
onScrolltolower(e: ScrollToLowerEvent) {
console.log("onScrolltolower 滚动到底部-----"+e.detail.direction)
}
}
};
</script>
<style>
.container{
display: flex;
flex-direction: column;
border:dashed;
flex: 1;
}
.scroll {
background-color: #eee;
position: relative;
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
border-color: red;
}
.scroll-item {
margin-left: 12rpx;
margin-right: 12rpx;
margin-top: 12rpx;
background-color: #fff;
border-radius: 8rpx;
}
.scroll-item-title {
width:100%;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: #555;
}
</style>
\ No newline at end of file
this.refresherrefresh = false
console.log("onRefresherrestore------下拉刷新被复位")
},
onRefresherpulling(e: RefresherEvent) {
console.log("onRefresherrestore------拉刷新控件被下拉-dy="+e.detail.dy)
},
onScrolltolower(e: ScrollToLowerEvent) {
console.log("onScrolltolower 滚动到底部-----"+e.detail.direction)
}
}
};
</script>
<style>
.container{
display: flex;
flex-direction: column;
border:dashed;
flex: 1;
}
.scroll {
background-color: #eee;
position: relative;
width: 100%;
flex: 1;
display: flex;
flex-direction: column;
border-color: red;
}
.scroll-item {
margin-left: 12rpx;
margin-right: 12rpx;
margin-top: 12rpx;
background-color: #fff;
border-radius: 8rpx;
}
.scroll-item-title {
width:100%;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: #555;
}
</style>
pages/component/sticky-header/sticky-header.uvue
浏览文件 @
15096a0a
<template>
<list-view :scroll-y="true" class="page" rebound="false" show-scrollbar=false :scroll-top="scroll_top_input" :refresher-enabled="refresher_enabled_boolean"
<template>
<list-view :scroll-y="true" class="page" rebound="false" show-scrollbar=false :scroll-top="scroll_top_input" :refresher-enabled="refresher_enabled_boolean"
:refresher-triggered="refresher_triggered_boolean" @refresherrefresh="list_view_refresherrefresh">
<list-item type = 1>
<swiper indicator-dots="true" circular="true">
...
...
@@ -9,9 +9,9 @@
</swiper-item>
</swiper>
</list-item>
<list-item class="content-item" type = 2>
<text class="text">向上滑动页面,体验sticky-header吸顶效果。</text>
</list-item>
<list-item class="content-item" type = 2>
<text class="text">向上滑动页面,体验sticky-header吸顶效果。</text>
</list-item>
<sticky-header>
<scroll-view style="background-color: #f5f5f5; flex-direction: row;" :scroll-x="true" :scroll-y="false" :show-scrollbar="false">
<view class="flex-row" style="align-self: flex-start; flex-direction: row;">
...
...
@@ -20,25 +20,25 @@
{{name}}
</text>
</view>
</scroll-view>
</sticky-header>
<list-item v-for="(item,index) in list_item" :key="index" class="content-item" type = 3>
<text class="text">{{item}}</text>
</list-item>
</list-view>
</template>
<script>
export default {
data() {
</scroll-view>
</sticky-header>
<list-item v-for="(item,index) in list_item" :key="index" class="content-item" type = 3>
<text class="text">{{item}}</text>
</list-item>
</list-view>
</template>
<script>
export default {
data() {
return {
sift_item: ["排序", "筛选"],
list_item: [] as Array<string>,
refresher_enabled_boolean: true,
sift_item: ["排序", "筛选"],
list_item: [] as Array<string>,
refresher_enabled_boolean: true,
refresher_triggered_boolean: false,
scroll_top_input: 0
}
scroll_top_input: 0
}
},
onLoad() {
let lists : Array<string> = []
...
...
@@ -46,47 +46,47 @@
lists.push("item---" + i)
}
this.list_item = lists
},
methods: {
list_view_refresherrefresh() {
console.log("下拉刷新被触发 ")
this.refresher_triggered_boolean = true
setTimeout(
function(){
this.refresher_triggered_boolean = false
}, 1500)
},
methods: {
list_view_refresherrefresh() {
console.log("下拉刷新被触发 ")
this.refresher_triggered_boolean = true
setTimeout(
() => {
this.refresher_triggered_boolean = false
}, 1500)
},
confirm_scroll_top_input(value : number) {
this.scroll_top_input = value
},
clickTH(index:number){
console.log("点击表头:" + index);
}
}
}
</script>
<style>
.page {
flex: 1;
background-color: #f5f5f5;
}
.content-item {
padding: 15px;
margin: 5px 0;
background-color: #fff;
}
.text {
font-size: 14px;
color: #666;
line-height: 20px;
}
}
}
</script>
<style>
.page {
flex: 1;
background-color: #f5f5f5;
}
.content-item {
padding: 15px;
margin: 5px 0;
background-color: #fff;
}
.text {
font-size: 14px;
color: #666;
line-height: 20px;
}
.sift-item {
color: #555;
font-size: 16px;
padding: 12px 15px;
}
}
</style>
pages/component/unicloud-db-contacts/detail.uvue
浏览文件 @
15096a0a
...
...
@@ -44,7 +44,7 @@
}
},
onLoad(options : Map<string, string>) {
this.$whereID = options
['id']
as string;
this.$whereID = options
.get('id')
as string;
this.where = `_id=='${this.$whereID}'`;
},
onShow() {
...
...
pages/component/unicloud-db-contacts/edit.uvue
浏览文件 @
15096a0a
...
...
@@ -54,7 +54,7 @@
}
},
onLoad(options : Map<string, string>) {
this.$whereID = options
['id']
as string;
this.$whereID = options
.get('id')
as string;
this.where = `_id=='${this.$whereID}'`;
},
onReady() {
...
...
@@ -167,4 +167,4 @@
.btn-delete {
margin-top: 15px;
}
</style>
\ No newline at end of file
</style>
pages/component/view/view.uvue
浏览文件 @
15096a0a
<template>
<page-head title="view"></page-head>
<view class="main" :hover-class="hover_class ? 'is-parent-hover' : 'none'">
<view class="test-view" :hover-class="hover_class ? 'is-hover' : 'none'" :hover-stop-propagation="stop_propagation"
:hover-start-time="start_time" :hover-stay-time="stay_time">
</view>
</view>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="content">
<boolean-data :defaultValue="false" title="是否指定按下去的样式类" @change="change_hover_class_boolean"></boolean-data>
<boolean-data :defaultValue="false" title="是否阻止本节点的祖先节点出现点击态"
@change="change_stop_propagation_boolean"></boolean-data>
<enum-data :items="start_time_enum" title="按住后多久出现点击态" @change="radio_change_start_time_enum"></enum-data>
<enum-data :items="stay_time_enum" title="手指松开后点击态保留时间" @change="radio_change_stay_time_enum"></enum-data>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script>
import { ItemType } from '@/components/enum-data/enum-data'
export default {
data() {
return {
hover_class: false,
stop_propagation: false,
start_time: 50,
stay_time: 400,
start_time_enum: [{ "value": 50, "name": "50毫秒" }, { "value": 200, "name": "200毫秒" }] as ItemType[],
stay_time_enum: [{ "value": 400, "name": "400毫秒" }, { "value": 200, "name": "200毫秒" }] as ItemType[]
}
},
methods: {
change_hover_class_boolean(checked : boolean) {
this.hover_class = checked
},
change_stop_propagation_boolean(checked : boolean) {
this.stop_propagation = checked
},
radio_change_start_time_enum(time : number) {
this.start_time = time
},
radio_change_stay_time_enum(time : number) {
this.stay_time = time
},
},
}
</script>
<style>
.main {
padding: 10rpx 0;
flex-direction: row;
justify-content: center;
}
.test-view {
height: 200px;
width: 200px;
background-color: white;
}
.text {
color: #777;
font-size: 26rpx;
}
.is-hover {
background-color: #179b16;
}
.is-parent-hover {
background-color: #aa0000;
}
</style>
\ No newline at end of file
<template>
<page-head title="view"></page-head>
<view class="main" :hover-class="hover_class ? 'is-parent-hover' : 'none'">
<view class="test-view" :hover-class="hover_class ? 'is-hover' : 'none'" :hover-stop-propagation="stop_propagation"
:hover-start-time="start_time" :hover-stay-time="stay_time">
</view>
</view>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="content">
<boolean-data :defaultValue="false" title="是否指定按下去的样式类" @change="change_hover_class_boolean"></boolean-data>
<boolean-data :defaultValue="false" title="是否阻止本节点的祖先节点出现点击态"
@change="change_stop_propagation_boolean"></boolean-data>
<enum-data :items="start_time_enum" title="按住后多久出现点击态" @change="radio_change_start_time_enum"></enum-data>
<enum-data :items="stay_time_enum" title="手指松开后点击态保留时间" @change="radio_change_stay_time_enum"></enum-data>
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script>
import { ItemType } from '@/components/enum-data/enum-data'
export default {
data() {
return {
hover_class: false,
stop_propagation: false,
start_time: 50,
stay_time: 400,
start_time_enum: [{ "value": 50, "name": "50毫秒" }, { "value": 200, "name": "200毫秒" }] as ItemType[],
stay_time_enum: [{ "value": 400, "name": "400毫秒" }, { "value": 200, "name": "200毫秒" }] as ItemType[]
}
},
methods: {
change_hover_class_boolean(checked : boolean) {
this.hover_class = checked
},
change_stop_propagation_boolean(checked : boolean) {
this.stop_propagation = checked
},
radio_change_start_time_enum(time : number) {
this.start_time = time
},
radio_change_stay_time_enum(time : number) {
this.stay_time = time
},
},
}
</script>
<style>
.main {
padding: 10rpx 0;
flex-direction: row;
justify-content: center;
}
.test-view {
height: 200px;
width: 200px;
background-color: white;
}
.text {
color: #777;
font-size: 26rpx;
}
.is-hover {
background-color: #179b16;
}
.is-parent-hover {
background-color: #aa0000;
}
</style>
pages/tabBar/component.uvue
浏览文件 @
15096a0a
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="uni-container">
<view class="uni-header-logo">
<image
class="uni-header-image"
src="/static/componentIndex.png"
></image>
</view>
<view class="uni-text-box">
<text class="hello-text"
>uni-app内置组件,展示样式仅供参考,文档详见:</text
>
<u-link
:href="'https://uniapp.dcloud.io/uni-app-x/component/'"
:text="'https://uniapp.dcloud.io/uni-app-x/component/'"
:inWhiteList="true"
></u-link>
</view>
<uni-collapse>
<template v-for="item in list" :key="item.id">
<uni-collapse-item :title="item.name" class="item">
<view
class="uni-navigate-item"
:hover-class="page.enable == false ? '' : 'is--active'"
v-for="(page, key) in item.pages"
:key="key"
@click="goDetailPage(page)"
>
<text
class="uni-navigate-text"
:class="page.enable == false ? 'text-disabled' : ''"
>{{ page.name }}</text
>
<image :src="arrowRightIcon" class="uni-icon"></image>
</view>
</uni-collapse-item>
</template>
</uni-collapse>
<template>
<!-- #ifdef APP -->
<scroll-view style="flex: 1">
<!-- #endif -->
<view class="uni-container">
<view class="uni-header-logo">
<image
class="uni-header-image"
src="/static/componentIndex.png"
></image>
</view>
<view class="uni-text-box">
<text class="hello-text"
>uni-app内置组件,展示样式仅供参考,文档详见:</text
>
<u-link
:href="'https://uniapp.dcloud.io/uni-app-x/component/'"
:text="'https://uniapp.dcloud.io/uni-app-x/component/'"
:inWhiteList="true"
></u-link>
</view>
<uni-collapse>
<template v-for="item in list" :key="item.id">
<uni-collapse-item :title="item.name" class="item">
<view
class="uni-navigate-item"
:hover-class="page.enable == false ? '' : 'is--active'"
v-for="(page, key) in item.pages"
:key="key"
@click="goDetailPage(page)"
>
<text
class="uni-navigate-text"
:class="page.enable == false ? 'text-disabled' : ''"
>{{ page.name }}</text
>
<image :src="arrowRightIcon" class="uni-icon"></image>
</view>
</uni-collapse-item>
</template>
</uni-collapse>
<!-- #ifdef UNI-APP-X && APP -->
<uni-upgrade-center-app ref="upgradePopup" @close="upgradePopupClose" />
<!-- #endif -->
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
<uni-upgrade-center-app ref="upgradePopup" @close="upgradePopupClose" />
<!-- #endif -->
</view>
<!-- #ifdef APP -->
</scroll-view>
<!-- #endif -->
</template>
<script lang="uts">
// #ifdef UNI-APP-X && APP
import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update'
// #endif
type Page = {
name : string
enable ?: boolean
url ?: string
}
type ListItem = {
id : string
name : string
pages : Page[]
url ?: string
enable ?: boolean
}
export default {
data() {
return {
list: [
{
id: 'view',
name: '视图容器',
pages: [
{
name: 'view',
},
{
name: 'scroll-view',
},
{
name: 'swiper',
},
/*
{
name: 'movable-view',
enable: false
},
{
name: 'cover-view',
enable: false
},
*/
{
name: 'list-view',
},
{
name: 'sticky-header',
},
{
name: 'sticky-section',
}
] as Page[],
},
{
id: 'content',
name: '基础内容',
pages: [
{
name: 'text',
},
{
name: 'rich-text',
enable: true,
},
{
name: 'progress',
},
] as Page[],
},
{
id: 'form',
name: '表单组件',
pages: [
{
name: 'button',
},
{
name: 'checkbox',
},
{
name: 'form',
},
{
name: 'input',
/* }, {
name: 'label',
enable: false
}, {
name: 'picker',
enable: false */
},
{
name: 'picker-view',
},
{
name: 'radio',
},
{
name: 'slider',
},
{
name: 'slider-100',
},
{
name: 'switch',
},
{
name: 'textarea',
},
/*
{
name: 'editor',
enable: false
},
*/
] as Page[],
},
{
id: 'nav',
name: '导航',
pages: [{
name: 'navigator',
enable: true
}] as Page[],
},
{
id: 'media',
name: '媒体组件',
pages: [
{
name: 'image',
enable: true,
},
{
name: 'video',
enable: true,
},
{
name: 'animation-view',
enable: false,
},
] as Page[],
},
/*
{
id: 'map',
name: '地图',
pages: [
{
name: 'map',
enable: false
}
] as Page[]
},
{
id: 'canvas',
name: '画布',
pages: [
{
name: 'canvas'
}
] as Page[]
},
*/
{
id: 'web-view',
name: '网页',
pages: [
{
name: '网络网页',
enable: true,
url: '/pages/component/web-view/web-view',
},
{
name: '本地网页',
enable: true,
url: '/pages/component/web-view-local/web-view-local',
},
] as Page[],
},
{
id: 'unicloud-db',
name: 'unicloud-db',
pages: [
{
name: '联系人',
enable: true,
url: '/pages/component/unicloud-db-contacts/list'
}
] as Page[],
},
/*
{
id: 'ad',
url: 'ad',
name: 'AD组件',
enable: false,
pages: [] as Page[]
}
*/
{
id: 'general-attr-event',
name: '通用属性和事件',
pages: [
{
name: '通用属性',
url: '/pages/component/general-attribute/general-attribute',
enable: true,
},
{
name: '通用事件',
url: '/pages/component/general-event/general-event',
enable: true,
import checkUpdate from '@/uni_modules/uni-upgrade-center-app/utils/check-update'
// #endif
type Page = {
name : string
enable ?: boolean
url ?: string
}
type ListItem = {
id : string
name : string
pages : Page[]
url ?: string
enable ?: boolean
}
export default {
data() {
return {
list: [
{
id: 'view',
name: '视图容器',
pages: [
{
name: 'view',
},
{
name: 'scroll-view',
},
{
name: 'swiper',
},
/*
{
name: 'movable-view',
enable: false
},
{
name: 'cover-view',
enable: false
},
*/
{
name: 'list-view',
},
{
name: 'sticky-header',
},
{
name: 'sticky-section',
}
] as Page[],
},
{
id: 'content',
name: '基础内容',
pages: [
{
name: 'text',
},
{
name: 'rich-text',
enable: true,
},
{
name: 'progress',
},
] as Page[],
},
{
id: 'form',
name: '表单组件',
pages: [
{
name: 'button',
},
{
name: 'checkbox',
},
{
name: 'form',
},
{
name: 'input',
/* }, {
name: 'label',
enable: false
}, {
name: 'picker',
enable: false */
},
{
name: 'picker-view',
},
{
name: 'radio',
},
{
name: 'slider',
},
{
name: 'slider-100',
},
{
name: 'switch',
},
{
name: 'textarea',
},
/*
{
name: 'editor',
enable: false
},
*/
] as Page[],
},
{
id: 'nav',
name: '导航',
pages: [{
name: 'navigator',
enable: true
}] as Page[],
},
{
id: 'media',
name: '媒体组件',
pages: [
{
name: 'image',
enable: true,
},
{
name: 'video',
enable: true,
},
{
name: 'animation-view',
enable: false,
},
] as Page[],
},
/*
{
id: 'map',
name: '地图',
pages: [
{
name: 'map',
enable: false
}
] as Page[]
},
{
id: 'canvas',
name: '画布',
pages: [
{
name: 'canvas'
}
] as Page[]
},
*/
{
id: 'web-view',
name: '网页',
pages: [
{
name: '网络网页',
enable: true,
url: '/pages/component/web-view/web-view',
},
{
name: '本地网页',
enable: true,
url: '/pages/component/web-view-local/web-view-local',
},
] as Page[],
},
// #ifdef APP
{
id: 'unicloud-db',
name: 'unicloud-db',
pages: [
{
name: '联系人',
enable: true,
url: '/pages/component/unicloud-db-contacts/list'
}
] as Page[],
},
// #endif
/*
{
id: 'ad',
url: 'ad',
name: 'AD组件',
enable: false,
pages: [] as Page[]
}
*/
{
id: 'general-attr-event',
name: '通用属性和事件',
pages: [
{
name: '通用属性',
url: '/pages/component/general-attribute/general-attribute',
enable: true,
},
{
name: '通用事件',
url: '/pages/component/general-event/general-event',
enable: true,
},
{
name: 'touch事件',
...
...
@@ -271,54 +273,54 @@ export default {
enable: true,
},
{
name: 'Transition事件',
url: '/pages/component/general-event/transition-event',
enable: true,
},
] as Page[],
}
] as ListItem[],
arrowUpIcon: '/static/icons/arrow-up.png',
arrowDownIcon: '/static/icons/arrow-down.png',
arrowRightIcon: '/static/icons/arrow-right.png',
}
},
methods: {
goDetailPage(e : Page) {
if (e.enable == false) {
uni.showToast({
title: '暂不支持',
icon: 'none',
})
return
}
const url =
e.url != null ? e.url! : `/pages/component/${e.name}/${e.name}`
uni.navigateTo({
url,
})
}
// #ifdef UNI-APP-X && APP
,upgradePopupClose() {
console.log('upgradePopup close');
}
// #endif
},
onReady() {
// #ifdef UNI-APP-X && APP
checkUpdate(this.$refs['upgradePopup'] as UniUpgradeCenterAppComponentPublicInstance)
// #endif
},
beforeUnmount() {
uni.showTabBar()?.catch(_ => {})
}
}
</script>
<style>
@import "../../common/uni-uvue.css";
.item {
margin-bottom: 12px;
}
</style>
name: 'Transition事件',
url: '/pages/component/general-event/transition-event',
enable: true,
},
] as Page[],
}
] as ListItem[],
arrowUpIcon: '/static/icons/arrow-up.png',
arrowDownIcon: '/static/icons/arrow-down.png',
arrowRightIcon: '/static/icons/arrow-right.png',
}
},
methods: {
goDetailPage(e : Page) {
if (e.enable == false) {
uni.showToast({
title: '暂不支持',
icon: 'none',
})
return
}
const url =
e.url != null ? e.url! : `/pages/component/${e.name}/${e.name}`
uni.navigateTo({
url,
})
}
// #ifdef UNI-APP-X && APP
,upgradePopupClose() {
console.log('upgradePopup close');
}
// #endif
},
onReady() {
// #ifdef UNI-APP-X && APP
checkUpdate(this.$refs['upgradePopup'] as UniUpgradeCenterAppComponentPublicInstance)
// #endif
},
beforeUnmount() {
uni.showTabBar()?.catch(_ => {})
}
}
</script>
<style>
@import "../../common/uni-uvue.css";
.item {
margin-bottom: 12px;
}
</style>
pages/template/calendar/calendar.uvue
浏览文件 @
15096a0a
<template>
<view class="root">
<view class="date">
<text class="date-text">{{
current_month}}</text>
</view>
<view ref="draw-header" class="calendar-header"></view>
<view ref="draw-weeks" class="calendar-week" @touchstart="select"></view>
<view class="btn-group">
<button size="mini" @click="preDate">上个月</button>
<button size="mini" @click="gotoToday">回到今天</button>
<button size="mini" @click="nextDate">下个月</button>
</view>
<view>{{
timeData.fullDate}} {{current_day}}</view>
</view>
</template>
<script>
import { Calendar, DateType } from './index.uts'
type CoordsType = {
x : number;
y : number;
width : number;
height : number;
data : DateType
}
export default {
data() {
return {
weeks: [] as Array<Array<DateType>>,
$coords: [] as Array<CoordsType>,
$calendar: new Calendar() as Calendar,
timeData: {
fullDate: '',
year: 0,
month: 0,
date: 0,
day: 0,
lunar: '',
disabled: false,
is_today: false
} as DateType
}
},
computed: {
// 获取月份
current_month() : string {
const nowDate = this.timeData
const month = nowDate.month
return month < 10 ? '0' + month : month.toString()
},
current_day() : string {
const time = this.timeData.data
if (time == null) {
return ''
}
return time.IMonthCn + time.IDayCn
}
},
created() { },
onReady() {
this.weeks = this.$calendar.getWeeks()
this.timeData = this.$calendar.getDateInfo()
// 绘制日历头部
this.drawHeader()
this.drawWeek(this.weeks, '')
},
methods: {
// 触发整个日历的点击事件,需要计算点击位置
select(event : TouchEvent) {
const refs = this.$refs['draw-weeks'] as Element
const rect = refs.getBoundingClientRect();
const dom_x = rect.left; // 元素左上角相对于视口的 X 坐标
const dom_y = rect.top; // 元素左上角相对于视口的 Y 坐标
const touch = event.touches[0];
const clientX = touch.clientX; // X 坐标
const clientY = touch.clientY; // Y 坐标
// 计算点击的相对位置
const x = clientX - dom_x
const y = clientY - dom_y
this.clickGrid(x, y)
},
// 点击具体的日历格子
clickGrid(x : number, y : number) {
// 小格子数组
const gridArray = this.$coords
// 遍历小格子数组,找到最接近点击坐标的小格子
for (let i = 0; i < gridArray.length; i++) {
const grid = gridArray[i]
// 计算小格子理论上的最大值
const max_x = grid.x + grid.width
const max_y = grid.y + grid.height
const is_x_limit = grid.x < x && x < max_x
const is_y_limit = grid.y < y && y < max_y
const is_select = is_x_limit && is_y_limit
if (is_select) {
const data = grid.data
this.timeData = this.$calendar.getDateInfo(data.fullDate)
this.drawWeek(this.weeks, grid.data.fullDate)
}
}
},
// 切换上个月
preDate() {
const fulldate = this.timeData.fullDate
const time = this.$calendar.getDate(fulldate, -1, 'month')
this.timeData = this.$calendar.getDateInfo(time.fullDate)
this.weeks = this.$calendar.getWeeks(time.fullDate)
// 重新绘制日历
this.drawWeek(this.weeks, time.fullDate)
},
// 切换下个他
nextDate() {
const fulldate = this.timeData.fullDate
const time = this.$calendar.getDate(fulldate, 1, 'month')
this.timeData = this.$calendar.getDateInfo(time.fullDate)
this.weeks = this.$calendar.getWeeks(time.fullDate)
// 重新绘制日历
this.drawWeek(this.weeks, time.fullDate)
},
// 回到今天
gotoToday() {
const time = this.$calendar.getDate()
this.timeData = this.$calendar.getDateInfo(time.fullDate)
this.weeks = this.$calendar.getWeeks(time.fullDate)
// 重新绘制日历
this.drawWeek(this.weeks, time.fullDate)
},
// 绘制日历顶部信息
drawHeader() {
const refs = this.$refs['draw-header'] as Element
let ctx = refs.getDrawableContext()
if (ctx == null) return
const date_header_map = ['一', '二', '三', '四', '五', '六', '日']
const width = refs.getBoundingClientRect().width
const num = date_header_map.length
const one_width = width / num
for (let i = 0; i < num; i++) {
let box_left = i * one_width + 2
let box_width = one_width - 4
let box_height = 26
// 文本赋值
const text = date_header_map[i]
let text_left = box_width / 2 + box_left
let text_top = box_height / 2 + 6
ctx.font = '12'
ctx.textAlign = 'center'
ctx.fillText(text, text_left, text_top)
ctx.update()
}
},
// 绘制日历主体
drawWeek(weeks : Array<Array<DateType>>, time : string) {
const start_time = Date.now()
const refs = this.$refs['draw-weeks'] as Element
let ctx = refs.getDrawableContext()
if (ctx == null) return
const dom = refs.getBoundingClientRect()
const width = dom.width
const height = dom.height
let week_len = weeks.length
const one_width = width / weeks[0].length
const one_height = height / week_len
if (time !== '') {
this.$coords = []
ctx.reset()
}
for (let week = 0; week < week_len; week++) {
const week_item = weeks[week]
for (let day = 0; day < week_item.length; day++) {
const day_item = week_item[day]
let day_left = day * one_width + 2
let day_top = one_height * week + 2
let day_width = one_width - 4
let day_height = one_height - 4
// 文本赋值
let text = day_item.date.toString()
let text_left = day * one_width + (one_width / 2)
let text_top = one_height * week + 25
ctx.font = '16'
ctx.textAlign = 'center'
// 日期是否禁用
if (day_item.disabled) {
ctx.fillStyle = '#ccc'
} else {
// 是否为今天
if (day_item.is_today) {
ctx.fillStyle = 'red'
} else {
// 是否为选中
if (time === day_item.fullDate) {
ctx.fillStyle = 'blue'
} else {
ctx.fillStyle = '#666'
}
}
// 第一次渲染获取数据
// if (time === '') {
// 存储坐标组,用于点击事件
const coords : CoordsType = {
x: day_left,
y: day_top,
width: day_width,
height: day_height,
data: day_item
}
this.$coords.push(coords)
// }
}
ctx.fillText(text, text_left, text_top)
text = day_item.lunar
let lunar_left = day * one_width + (one_width / 2)
let lunar_top = one_height * week + 42
ctx.font = '10'
ctx.textAlign = 'center'
ctx.fillText(text, lunar_left, lunar_top)
}
}
ctx.update()
console.log('diff time', Date.now() - start_time);
}
}
}
</script>
<style>
.root {
flex: 1;
position: relative;
padding: 15px;
background-color: #fff;
}
.calendar-header {
height: 30px;
margin-bottom: 10px;
}
.date {
margin-bottom: 10px;
margin-left: 10px;
}
.date-text {
font-size: 34px;
font-weight: bold;
}
.calendar-week {
height: 350px;
margin: 2px 0;
}
.btn-group {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 20px 0;
}
<template>
<view class="root">
<view class="date">
<text class="date-text">{{
current_month }}</text>
</view>
<view ref="draw-header" class="calendar-header"></view>
<view ref="draw-weeks" class="calendar-week" @touchstart="select"></view>
<view class="btn-group">
<button size="mini" @click="preDate">上个月</button>
<button size="mini" @click="gotoToday">回到今天</button>
<button size="mini" @click="nextDate">下个月</button>
</view>
<view>{{
timeData.fullDate }} {{ current_day }}</view>
</view>
</template>
<script>
import { Calendar, DateType } from './index.uts'
type CoordsType = {
x: number;
y: number;
width: number;
height: number;
data: DateType
}
export default {
data () {
return {
weeks: [] as Array<Array<DateType>>,
$coords: [] as Array<CoordsType>,
$calendar: new Calendar() as Calendar,
timeData: {
fullDate: '',
year: 0,
month: 0,
date: 0,
day: 0,
lunar: '',
disabled: false,
is_today: false
} as DateType
}
},
computed: {
// 获取月份
current_month (): string {
const nowDate = this.timeData
const month = nowDate.month
return month < 10 ? '0' + month : month.toString()
},
current_day (): string {
const time = this.timeData.data
if (time == null) {
return ''
}
return time.IMonthCn + time.IDayCn
}
},
created () { },
onReady () {
this.weeks = this.$data.$calendar.getWeeks()
this.timeData = this.$data.$calendar.getDateInfo()
// 绘制日历头部
this.drawHeader()
this.drawWeek(this.weeks, '')
},
methods: {
// 触发整个日历的点击事件,需要计算点击位置
select (event: TouchEvent) {
const refs = this.$refs['draw-weeks'] as Element
const rect = refs.getBoundingClientRect();
const dom_x = rect.left; // 元素左上角相对于视口的 X 坐标
const dom_y = rect.top; // 元素左上角相对于视口的 Y 坐标
const touch = event.touches[0];
const clientX = touch.clientX; // X 坐标
const clientY = touch.clientY; // Y 坐标
// 计算点击的相对位置
const x = clientX - dom_x
const y = clientY - dom_y
this.clickGrid(x, y)
},
// 点击具体的日历格子
clickGrid (x: number, y: number) {
// 小格子数组
const gridArray = this.$coords
// 遍历小格子数组,找到最接近点击坐标的小格子
for (let i = 0; i < gridArray.length; i++) {
const grid = gridArray[i]
// 计算小格子理论上的最大值
const max_x = grid.x + grid.width
const max_y = grid.y + grid.height
const is_x_limit = grid.x < x && x < max_x
const is_y_limit = grid.y < y && y < max_y
const is_select = is_x_limit && is_y_limit
if (is_select) {
const data = grid.data
this.timeData = this.$calendar.getDateInfo(data.fullDate)
this.drawWeek(this.weeks, grid.data.fullDate)
}
}
},
// 切换上个月
preDate () {
const fulldate = this.timeData.fullDate
const time = this.$calendar.getDate(fulldate, -1, 'month')
this.timeData = this.$calendar.getDateInfo(time.fullDate)
this.weeks = this.$calendar.getWeeks(time.fullDate)
// 重新绘制日历
this.drawWeek(this.weeks, time.fullDate)
},
// 切换下个他
nextDate () {
const fulldate = this.timeData.fullDate
const time = this.$calendar.getDate(fulldate, 1, 'month')
this.timeData = this.$calendar.getDateInfo(time.fullDate)
this.weeks = this.$calendar.getWeeks(time.fullDate)
// 重新绘制日历
this.drawWeek(this.weeks, time.fullDate)
},
// 回到今天
gotoToday () {
const time = this.$calendar.getDate()
this.timeData = this.$calendar.getDateInfo(time.fullDate)
this.weeks = this.$calendar.getWeeks(time.fullDate)
// 重新绘制日历
this.drawWeek(this.weeks, time.fullDate)
},
// 绘制日历顶部信息
drawHeader () {
const refs = this.$refs['draw-header'] as Element
let ctx = refs.getDrawableContext()
if (ctx == null) return
const date_header_map = ['一', '二', '三', '四', '五', '六', '日']
const width = refs.getBoundingClientRect().width
const num = date_header_map.length
const one_width = width / num
for (let i = 0; i < num; i++) {
let box_left = i * one_width + 2
let box_width = one_width - 4
let box_height = 26
// 文本赋值
const text = date_header_map[i]
let text_left = box_width / 2 + box_left
let text_top = box_height / 2 + 6
ctx.font = '12'
ctx.textAlign = 'center'
ctx.fillText(text, text_left, text_top)
ctx.update()
}
},
// 绘制日历主体
drawWeek (weeks: Array<Array<DateType>>, time: string) {
const start_time = Date.now()
const refs = this.$refs['draw-weeks'] as Element
let ctx = refs.getDrawableContext()
if (ctx == null) return
const dom = refs.getBoundingClientRect()
const width = dom.width
const height = dom.height
let week_len = weeks.length
const one_width = width / weeks[0].length
const one_height = height / week_len
if (time !== '') {
this.$coords = []
ctx.reset()
}
for (let week = 0; week < week_len; week++) {
const week_item = weeks[week]
for (let day = 0; day < week_item.length; day++) {
const day_item = week_item[day]
let day_left = day * one_width + 2
let day_top = one_height * week + 2
let day_width = one_width - 4
let day_height = one_height - 4
// 文本赋值
let text = day_item.date.toString()
let text_left = day * one_width + (one_width / 2)
let text_top = one_height * week + 25
ctx.font = '16'
ctx.textAlign = 'center'
// 日期是否禁用
if (day_item.disabled) {
ctx.fillStyle = '#ccc'
} else {
// 是否为今天
if (day_item.is_today) {
ctx.fillStyle = 'red'
} else {
// 是否为选中
if (time === day_item.fullDate) {
ctx.fillStyle = 'blue'
} else {
ctx.fillStyle = '#666'
}
}
// 第一次渲染获取数据
// if (time === '') {
// 存储坐标组,用于点击事件
const coords: CoordsType = {
x: day_left,
y: day_top,
width: day_width,
height: day_height,
data: day_item
}
this.$coords.push(coords)
// }
}
ctx.fillText(text, text_left, text_top)
text = day_item.lunar
let lunar_left = day * one_width + (one_width / 2)
let lunar_top = one_height * week + 42
ctx.font = '10'
ctx.textAlign = 'center'
ctx.fillText(text, lunar_left, lunar_top)
}
}
ctx.update()
console.log('diff time', Date.now() - start_time);
}
}
}
</script>
<style>
.root {
flex: 1;
position: relative;
padding: 15px;
background-color: #fff;
}
.calendar-header {
height: 30px;
margin-bottom: 10px;
}
.date {
margin-bottom: 10px;
margin-left: 10px;
}
.date-text {
font-size: 34px;
font-weight: bold;
}
.calendar-week {
height: 350px;
margin: 2px 0;
}
.btn-group {
display: flex;
flex-direction: row;
justify-content: space-between;
margin: 20px 0;
}
</style>
pages/template/custom-tab-bar/custom-tab-bar.uvue
浏览文件 @
15096a0a
...
...
@@ -55,10 +55,12 @@
return {
tabList: [
{
init: false
init: false,
preload: false
} as TabItem,
{
init: false
init: false,
preload: false
} as TabItem,
] as TabItem[],
selectedIndex: -1,
...
...
pages/template/drop-card/card/card.uvue
浏览文件 @
15096a0a
<template>
<view class="card" ref="card" @touchstart="touchstart($event as TouchEvent)"
@touchmove="touchmove($event as TouchEvent)" @touchend="touchend" @touchcancel="touchend">
<image class="card-img" ref="card-img" :src="img"></image>
<view class="state">
<image class="state-icon like" ref="state-icon-like" src="/static/template/drop-card/like.png" mode="widthFix">
</image>
<image class="state-icon dislike" ref="state-icon-dislike" src="/static/template/drop-card/dislike.png"
mode="widthFix"></image>
<!-- cardIndex:{{cardIndex}} -->
</view>
</view>
</template>
<script>
let sX : number = 0,
sY : number = 0,
screenWidth : number = 1,
screenHeight : number = 1,
floating : boolean = false,
touchstartAfter : boolean = false;
export default {
data() {
return {
$elementMap: new Map<string, Element>(),
x: 0 as number,
y: 0 as number,
// 飘走的卡片计数
floatCount: 0 as number
}
},
props: {
img: {
type: String,
default: "/static/template/drop-card/1.jpg"
},
cardIndex: {
type: Number,
default: 0
}
},
computed: {
movePercent() : number {
return Math.abs(this.x) / (screenWidth / 2 * 3)
},
likeOpacity() : number {
return this.x < 0 ? 0 : this.movePercent * 100
},
dislikeOpacity() : number {
return this.x > 0 ? 0 : this.movePercent * 100
}
},
mounted() {
uni.getSystemInfo({
success: (e) => {
screenWidth = e.screenWidth;
screenHeight = e.screenHeight;
}
})
// TODO 需要延迟设置才能生效
setTimeout(() => {
this.setElementStyle('card', 'height', screenHeight * 0.7 + 'px');
this.setElementStyle('card-img', 'height', screenHeight * 0.7 + 'px');
this.initCardStyle()
}, 200)
uni.$on('uni-drop-card-float', () => {
this.floatCount++
this.initCardStyle()
})
},
methods: {
initCardStyle() {
let _index = (this.cardIndex + this.floatCount) % 3
// console.log('~~~~~~_index:'+_index + ' cardIndex:'+this.cardIndex+' floatCount:'+this.floatCount);
this.setElementStyle('card', 'z-index', _index)
this.setElementStyle('card', 'margin-top', screenHeight * 0.15 - 30 * _index + 'px');
this.setElementStyle('card', 'transform', 'scale(' + (0.9 + 0.05 * _index) + ')')
},
// 工具方法,用于快速设置 Element 的 style
setElementStyle(refName : string, propertyName : string, propertyStyle : any) : void {
const elementMap = this.$data['$elementMap'] as Map<string, Element>
let element : Element | null = elementMap[refName]
if (element == null) {
element = this.$refs[refName] as Element;
elementMap[refName] = element
} else {
// console.log('直接拿');
}
element?.style?.setProperty(propertyName, propertyStyle);
},
touchstart(e : TouchEvent) {
// console.log('touchstart')
if (floating) {
return // 浮动动画进行中
}
sX = e.touches[0].screenX;
sY = e.touches[0].screenY;
this.x = 0
this.y = 0
touchstartAfter = true
},
touchmove(e : TouchEvent) {
// console.log('touchmove')
if (!touchstartAfter || floating) {
return // floating:浮动动画进行中
}
this.x += e.touches[0].screenX - sX;
this.y += e.touches[0].screenY - sY;
sX = e.touches[0].screenX;
sY = e.touches[0].screenY;
this.moveCard()
},
touchend() {
// console.log('touchend')
touchstartAfter = false
if (floating) {
return // 浮动动画进行中
}
floating = true
// 设置释放之后飘走的方向 0回到坐标中心 1向右 2向左
let k : number = 0;
if (this.x > screenWidth / 10) {
k = 1
} else if (this.x < screenWidth * -1 / 10) {
k = -1
}
function cardTo(x : number, y : number, callback : () => void, speed : number = 10) {
let interval : number = 0
let acceleration : number = 1
interval = setInterval(() => {
// 加速度
acceleration += 0.2
const dx = x - this.x
if (Math.abs(dx) < 1) {
this.x = x
} else {
this.x += dx / speed * acceleration
}
const dy = y - this.y
if (Math.abs(dy) < 1) {
this.y = y
} else {
this.y += dy / speed * acceleration
}
this.moveCard()
if (this.x == x && this.y == y) {
clearInterval(interval)
callback()
}
}, 16)
}
if (k.toInt() != 0) {
cardTo(k * screenWidth * 1.3, this.y * 3, () => {
// 状态图标变回透明
this.setElementStyle("state-icon-like", 'opacity', 0)
this.setElementStyle("state-icon-dislike", 'opacity', 0)
// 设置为透明,防止飘回时因为 margin-top 太高,露出来
this.setElementStyle("card", 'opacity', 0)
setTimeout(() => {
this.setElementStyle("card", 'opacity', 1)
}, 300)
// 执行卡片飘动后事件,注意uni.$emit是全局事件。其他卡片也会执行
uni.$emit('uni-drop-card-float', null)
floating = false
}, 8)
} else {
const _x : number = this.x
const _y : number = this.y
cardTo((_x * -0.05).toInt(), (_y * -0.05).toInt(), () => {
cardTo(0, 0, () => {
console.log('bounce')
floating = false
}, 30)
})
}
},
moveCard() {
this.setElementStyle("card",
'transform',
`translate(${this.x}px,${this.y}px) rotate(${this.x / -30}deg) scale(1)`
)
this.setElementStyle("state-icon-like", 'opacity', this.x < 0 ? 0 : this.movePercent * 10)
this.setElementStyle("state-icon-dislike", 'opacity', this.x > 0 ? 0 : this.movePercent * 10)
}
}
}
</script>
<style>
.card {
width: 700rpx;
height: 750rpx;
position: absolute;
top: 0;
left: 0;
margin: 0 25rpx;
margin-top: 50px;
border-radius: 10px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
background-color: #FFF;
transition: margin-top 300ms;
transition-timing-function: ease-in;
}
.card-img {
width: 700rpx;
height: 750rpx;
border-radius: 10px;
}
.state {
top: 20rpx;
left: 20rpx;
width: 650rpx;
padding: 4px;
position: absolute;
flex-direction: row;
justify-content: space-between;
}
.state-icon {
width: 30px;
height: 30px;
border: 1px solid #FFF;
background-color: #FFF;
padding: 3px;
border-radius: 100px;
box-shadow: 0 0 1px #EBEBEB;
opacity: 0;
}
</style>
\ No newline at end of file
<template>
<view class="card" ref="card" @touchstart="touchstart($event as TouchEvent)"
@touchmove="touchmove($event as TouchEvent)" @touchend="touchend" @touchcancel="touchend">
<image class="card-img" ref="card-img" :src="img"></image>
<view class="state">
<image class="state-icon like" ref="state-icon-like" src="/static/template/drop-card/like.png" mode="widthFix">
</image>
<image class="state-icon dislike" ref="state-icon-dislike" src="/static/template/drop-card/dislike.png"
mode="widthFix"></image>
<!-- cardIndex:{{cardIndex}} -->
</view>
</view>
</template>
<script>
let sX : number = 0,
sY : number = 0,
screenWidth : number = 1,
screenHeight : number = 1,
floating : boolean = false,
touchstartAfter : boolean = false;
export default {
data() {
return {
$elementMap: new Map<string, Element>(),
x: 0 as number,
y: 0 as number,
// 飘走的卡片计数
floatCount: 0 as number
}
},
props: {
img: {
type: String,
default: "/static/template/drop-card/1.jpg"
},
cardIndex: {
type: Number,
default: 0
}
},
computed: {
movePercent() : number {
return Math.abs(this.x) / (screenWidth / 2 * 3)
},
likeOpacity() : number {
return this.x < 0 ? 0 : this.movePercent * 100
},
dislikeOpacity() : number {
return this.x > 0 ? 0 : this.movePercent * 100
}
},
mounted() {
uni.getSystemInfo({
success: (e) => {
screenWidth = e.screenWidth;
screenHeight = e.screenHeight;
}
})
// TODO 需要延迟设置才能生效
setTimeout(() => {
this.setElementStyle('card', 'height', screenHeight * 0.7 + 'px');
this.setElementStyle('card-img', 'height', screenHeight * 0.7 + 'px');
this.initCardStyle()
}, 200)
uni.$on('uni-drop-card-float', () => {
this.floatCount++
this.initCardStyle()
})
},
methods: {
initCardStyle() {
let _index = (this.cardIndex + this.floatCount) % 3
// console.log('~~~~~~_index:'+_index + ' cardIndex:'+this.cardIndex+' floatCount:'+this.floatCount);
this.setElementStyle('card', 'z-index', _index)
this.setElementStyle('card', 'margin-top', screenHeight * 0.15 - 30 * _index + 'px');
this.setElementStyle('card', 'transform', 'scale(' + (0.9 + 0.05 * _index) + ')')
},
// 工具方法,用于快速设置 Element 的 style
setElementStyle(refName : string, propertyName : string, propertyStyle : any) : void {
const elementMap = this.$data['$elementMap'] as Map<string, Element>
let element : Element | null = elementMap.get(refName)
if (element == null) {
element = this.$refs[refName] as Element;
elementMap.set(refName, element)
} else {
// console.log('直接拿');
}
element?.style?.setProperty(propertyName, propertyStyle);
},
touchstart(e : TouchEvent) {
// console.log('touchstart')
if (floating) {
return // 浮动动画进行中
}
sX = e.touches[0].screenX;
sY = e.touches[0].screenY;
this.x = 0
this.y = 0
touchstartAfter = true
},
touchmove(e : TouchEvent) {
// console.log('touchmove')
if (!touchstartAfter || floating) {
return // floating:浮动动画进行中
}
this.x += e.touches[0].screenX - sX;
this.y += e.touches[0].screenY - sY;
sX = e.touches[0].screenX;
sY = e.touches[0].screenY;
this.moveCard()
},
touchend() {
// console.log('touchend')
touchstartAfter = false
if (floating) {
return // 浮动动画进行中
}
floating = true
// 设置释放之后飘走的方向 0回到坐标中心 1向右 2向左
let k : number = 0;
if (this.x > screenWidth / 10) {
k = 1
} else if (this.x < screenWidth * -1 / 10) {
k = -1
}
const _this = this
function cardTo(x : number, y : number, callback : () => void, speed : number = 10) {
let interval : number = 0
let acceleration : number = 1
interval = setInterval(() => {
// 加速度
acceleration += 0.2
const dx = x - _this.x
if (Math.abs(dx) < 1) {
_this.x = x
} else {
_this.x += dx / speed * acceleration
}
const dy = y - _this.y
if (Math.abs(dy) < 1) {
_this.y = y
} else {
_this.y += dy / speed * acceleration
}
_this.moveCard()
if (_this.x == x && _this.y == y) {
clearInterval(interval)
callback()
}
}, 16)
}
if (k.toInt() != 0) {
cardTo(k * screenWidth * 1.3, this.y * 3, () => {
// 状态图标变回透明
this.setElementStyle("state-icon-like", 'opacity', 0)
this.setElementStyle("state-icon-dislike", 'opacity', 0)
// 设置为透明,防止飘回时因为 margin-top 太高,露出来
this.setElementStyle("card", 'opacity', 0)
setTimeout(() => {
this.setElementStyle("card", 'opacity', 1)
}, 300)
// 执行卡片飘动后事件,注意uni.$emit是全局事件。其他卡片也会执行
uni.$emit('uni-drop-card-float', null)
floating = false
}, 8)
} else {
const _x : number = this.x
const _y : number = this.y
cardTo((_x * -0.05).toInt(), (_y * -0.05).toInt(), () => {
cardTo(0, 0, () => {
console.log('bounce')
floating = false
}, 30)
})
}
},
moveCard() {
this.setElementStyle("card",
'transform',
`translate(${this.x}px,${this.y}px) rotate(${this.x / -30}deg) scale(1)`
)
this.setElementStyle("state-icon-like", 'opacity', this.x < 0 ? 0 : this.movePercent * 10)
this.setElementStyle("state-icon-dislike", 'opacity', this.x > 0 ? 0 : this.movePercent * 10)
}
}
}
</script>
<style>
.card {
width: 700rpx;
height: 750rpx;
position: absolute;
top: 0;
left: 0;
margin: 0 25rpx;
margin-top: 50px;
border-radius: 10px;
box-shadow: 0 0 5px rgba(0, 0, 0, 0.1);
background-color: #FFF;
transition: margin-top 300ms;
transition-timing-function: ease-in;
}
.card-img {
width: 700rpx;
height: 750rpx;
border-radius: 10px;
}
.state {
top: 20rpx;
left: 20rpx;
width: 650rpx;
padding: 4px;
position: absolute;
flex-direction: row;
justify-content: space-between;
}
.state-icon {
width: 30px;
height: 30px;
border: 1px solid #FFF;
background-color: #FFF;
padding: 3px;
border-radius: 100px;
box-shadow: 0 0 1px #EBEBEB;
opacity: 0;
}
</style>
pages/template/list-news/detail/detail.uvue
浏览文件 @
15096a0a
...
...
@@ -22,7 +22,7 @@
post_id: "",
}
},
onLoad(event) {
onLoad(event
: OnLoadOptions
) {
if (event != null) {
this.post_id = event["post_id"] ?? "";
this.cover = event["cover"] ?? "";
...
...
pages/template/long-list/long-list.uvue
浏览文件 @
15096a0a
<template>
<scroll-view ref="pageScrollView" class="page" :scroll-y="true" :rebound="false"
@startnestedscroll="onStartNestedScroll" @nestedprescroll="onNestedPreScroll">
<view ref="header" class="search-bar">
<input placeholder="搜索..." />
</view>
<view class="swiper-list">
<scroll-view ref="tabScroll" class="swiper-tabs" :scroll-x="true" :show-scrollbar="false">
<view class="flex-row" style="align-self: flex-start;">
<text ref="swipertab" class="swiper-tabs-item" :class="swiperIndex==index ? 'swiper-tabs-item-active' : ''"
v-for="(item, index) in swiperList" :key="index" @click="onTabClick(index)">
{{item.name}}
</text>
</view>
<view ref="indicator" class="swiper-tabs-indicator"></view>
</scroll-view>
<swiper ref="swiper" class="swiper-view" :current="swiperIndex" @transition="onSwiperTransition"
@animationfinish="onSwiperAnimationfinish">
<swiper-item class="swiper-item" v-for="(item, index) in swiperList" :key="index">
<long-page ref="longPage" :type="item.type" :preload="item.preload"></long-page>
</swiper-item>
</swiper>
</view>
</scroll-view>
</template>
<script>
import longPage from './long-list-page.uvue';
type SwiperTabsItem = {
x : number,
w : number
}
type SwiperViewItem = {
type : string,
name : string,
preload : boolean,
}
/**
* 根据权重在两个值之间执行线性插值.
* @constructor
* @param {number} value1 - 第一个值,该值应为下限.
* @param {number} value2 - 第二个值,该值应为上限.
* @param {number} amount - 应介于 0 和 1 之间,指示内插的权重.
* @returns {number} 内插值
*/
function lerpNumber(value1 : number, value2 : number, amount : number) : number {
return (value1 + (value2 - value1) * amount)
}
export default {
components: {
longPage
},
data() {
return {
swiperList: [
{
type: 'UpdatedDate',
name: '最新上架',
preload: true
} as SwiperViewItem,
{
type: 'FreeHot',
name: '免费热榜'
} as SwiperViewItem,
{
type: 'PaymentHot',
name: '付费热榜'
} as SwiperViewItem,
{
type: 'HotList',
name: '热门总榜'
} as SwiperViewItem
] as SwiperViewItem[],
swiperIndex: -1,
$pageScrollView: null as null | Element,
$headerHeight: 0,
$animationFinishIndex: 0,
$tabScrollView: null as null | Element,
$indicatorNode: null as null | Element,
$swiperWidth: 0,
$swiperTabsRect: [] as SwiperTabsItem[]
}
},
onReady() {
this.$pageScrollView = this.$refs['pageScrollView'] as Element;
this.$headerHeight = (this.$refs['header'] as Element).offsetHeight
this.$swiperWidth = (this.$refs['swiper'] as Element).getBoundingClientRect().width
this.$tabScrollView = this.$refs['tabScroll'] as Element
this.$indicatorNode = this.$refs['indicator'] as Element
this.cacheTabItemsSize()
this.setSwiperIndex(0, true)
},
onPullDownRefresh() {
(this.$refs["longPage"]! as ComponentPublicInstance[])[this.swiperIndex].$callMethod('refreshData', () => {
uni.stopPullDownRefresh()
})
},
methods: {
// TODO
onStartNestedScroll(_ : StartNestedScrollEvent) : boolean {
return true
},
onNestedPreScroll(event : NestedPreScrollEvent) {
const deltaY = event.deltaY
const scrollTop = this.$pageScrollView!.scrollTop
// 优先处理父容器滚动,父容器不能滚动时滚动子
if (deltaY > 0) {
// 向上滚动,如果父容器 header scrollTop < offsetHeight,先滚动父容器
if (scrollTop < this.$headerHeight) {
const difference = this.$headerHeight - scrollTop - deltaY
if (difference > 0) {
this.$pageScrollView!.scrollBy(event.deltaX, deltaY)
event.consumed(event.deltaX, deltaY)
} else {
const top : number = deltaY + difference
event.consumed(event.deltaX, top.toFloat())
this.$pageScrollView!.scrollBy(event.deltaX, top.toFloat())
}
}
} else if (deltaY < 0) {
// 向下滚动,如果父容器 scrollTop > 0,通知子被父容器消耗,然后滚动到 0
if (scrollTop > 0) {
event.consumed(event.deltaX, deltaY)
this.$pageScrollView!.scrollBy(event.deltaX, deltaY)
}
}
},
onTabClick(index : number) {
this.setSwiperIndex(index, false)
},
onSwiperTransition(e : SwiperTransitionEvent) {
// 微信 skyline 每项完成触发 Animationfinish,偏移值重置
// 微信 webview 全部完成触发 Animationfinish,偏移值累加
// 在滑动到下一个项的过程中,再次反向滑动,偏移值递减
// uni-app-x 和微信 webview 行为一致
const offset_x = e.detail.dx
// 计算当前索引并重置差异
const current_offset_x = offset_x % this.$swiperWidth
const current_offset_i = offset_x / this.$swiperWidth
const current_index = this.$animationFinishIndex + current_offset_i.toInt()
// 计算目标索引及边界检查
let move_to_index = current_index
if (current_offset_x > 0 && move_to_index < this.swiperList.length - 1) {
move_to_index += 1
} else if (current_offset_x < 0 && move_to_index > 0) {
move_to_index -= 1
}
// 计算偏移百分比
const percentage = Math.abs(current_offset_x) / this.$swiperWidth
// 通知更新指示线
this.updateTabIndicator(current_index, move_to_index, percentage)
// 首次可见时初始化数据
this.initSwiperItemData(move_to_index)
},
onSwiperAnimationfinish(e : SwiperAnimationFinishEvent) {
this.setSwiperIndex(e.detail.current, true)
this.$animationFinishIndex = e.detail.current
},
cacheTabItemsSize() {
this.$swiperTabsRect.length = 0
const tabs = this.$refs["swipertab"] as Element[]
tabs.forEach((node) => {
this.$swiperTabsRect.push({
x: node.offsetLeft,
w: node.offsetWidth
} as SwiperTabsItem)
})
},
setSwiperIndex(index : number, updateIndicator : boolean) {
if (this.swiperIndex === index) {
return
}
this.swiperIndex = index
this.initSwiperItemData(index)
if (updateIndicator) {
this.updateTabIndicator(index, index, 1)
}
},
updateTabIndicator(current_index : number, move_to_index : number, percentage : number) {
const current_size = this.$swiperTabsRect[current_index]
const move_to_size = this.$swiperTabsRect[move_to_index]
// 计算指示线 左边距 和 宽度 在移动过程中的线性值
const indicator_line_x = lerpNumber(current_size.x, move_to_size.x, percentage)
const indicator_line_w = lerpNumber(current_size.w, move_to_size.w, percentage)
// 更新指示线
const x = indicator_line_x + indicator_line_w / 2
this.$indicatorNode?.style?.setProperty('transform', `translateX(${x}px) scaleX(${indicator_line_w})`)
// 滚动到水平中心位置
<template>
<scroll-view ref="pageScrollView" class="page" :scroll-y="true" :rebound="false"
@startnestedscroll="onStartNestedScroll" @nestedprescroll="onNestedPreScroll">
<view ref="header" class="search-bar">
<input placeholder="搜索..." />
</view>
<view class="swiper-list">
<scroll-view ref="tabScroll" class="swiper-tabs" :scroll-x="true" :show-scrollbar="false">
<view class="flex-row" style="align-self: flex-start;">
<text ref="swipertab" class="swiper-tabs-item" :class="swiperIndex==index ? 'swiper-tabs-item-active' : ''"
v-for="(item, index) in swiperList" :key="index" @click="onTabClick(index)">
{{item.name}}
</text>
</view>
<view ref="indicator" class="swiper-tabs-indicator"></view>
</scroll-view>
<swiper ref="swiper" class="swiper-view" :current="swiperIndex" @transition="onSwiperTransition"
@animationfinish="onSwiperAnimationfinish">
<swiper-item class="swiper-item" v-for="(item, index) in swiperList" :key="index">
<long-page ref="longPage" :type="item.type" :preload="item.preload"></long-page>
</swiper-item>
</swiper>
</view>
</scroll-view>
</template>
<script>
import longPage from './long-list-page.uvue';
type SwiperTabsItem = {
x : number,
w : number
}
type SwiperViewItem = {
type : string,
name : string,
preload : boolean,
}
/**
* 根据权重在两个值之间执行线性插值.
* @constructor
* @param {number} value1 - 第一个值,该值应为下限.
* @param {number} value2 - 第二个值,该值应为上限.
* @param {number} amount - 应介于 0 和 1 之间,指示内插的权重.
* @returns {number} 内插值
*/
function lerpNumber(value1 : number, value2 : number, amount : number) : number {
return (value1 + (value2 - value1) * amount)
}
export default {
components: {
longPage
},
data() {
return {
swiperList: [
{
type: 'UpdatedDate',
name: '最新上架',
preload: true
} as SwiperViewItem,
{
type: 'FreeHot',
name: '免费热榜',
preload: false
} as SwiperViewItem,
{
type: 'PaymentHot',
name: '付费热榜',
preload: false
} as SwiperViewItem,
{
type: 'HotList',
name: '热门总榜',
preload: false
} as SwiperViewItem
] as SwiperViewItem[],
swiperIndex: -1,
$pageScrollView: null as null | Element,
$headerHeight: 0,
$animationFinishIndex: 0,
$tabScrollView: null as null | Element,
$indicatorNode: null as null | Element,
$swiperWidth: 0,
$swiperTabsRect: [] as SwiperTabsItem[]
}
},
onReady() {
this.$pageScrollView = this.$refs['pageScrollView'] as Element;
this.$headerHeight = (this.$refs['header'] as Element).offsetHeight
this.$swiperWidth = (this.$refs['swiper'] as Element).getBoundingClientRect().width
this.$tabScrollView = this.$refs['tabScroll'] as Element
this.$indicatorNode = this.$refs['indicator'] as Element
this.cacheTabItemsSize()
this.setSwiperIndex(0, true)
},
onPullDownRefresh() {
(this.$refs["longPage"]! as ComponentPublicInstance[])[this.swiperIndex].$callMethod('refreshData', () => {
uni.stopPullDownRefresh()
})
},
methods: {
// TODO
onStartNestedScroll(_ : StartNestedScrollEvent) : boolean {
return true
},
onNestedPreScroll(event : NestedPreScrollEvent) {
const deltaY = event.deltaY
const scrollTop = this.$pageScrollView!.scrollTop
// 优先处理父容器滚动,父容器不能滚动时滚动子
if (deltaY > 0) {
// 向上滚动,如果父容器 header scrollTop < offsetHeight,先滚动父容器
if (scrollTop < this.$headerHeight) {
const difference = this.$headerHeight - scrollTop - deltaY
if (difference > 0) {
this.$pageScrollView!.scrollBy(event.deltaX, deltaY)
event.consumed(event.deltaX, deltaY)
} else {
const top : number = deltaY + difference
event.consumed(event.deltaX, top.toFloat())
this.$pageScrollView!.scrollBy(event.deltaX, top.toFloat())
}
}
} else if (deltaY < 0) {
// 向下滚动,如果父容器 scrollTop > 0,通知子被父容器消耗,然后滚动到 0
if (scrollTop > 0) {
event.consumed(event.deltaX, deltaY)
this.$pageScrollView!.scrollBy(event.deltaX, deltaY)
}
}
},
onTabClick(index : number) {
this.setSwiperIndex(index, false)
},
onSwiperTransition(e : SwiperTransitionEvent) {
// 微信 skyline 每项完成触发 Animationfinish,偏移值重置
// 微信 webview 全部完成触发 Animationfinish,偏移值累加
// 在滑动到下一个项的过程中,再次反向滑动,偏移值递减
// uni-app-x 和微信 webview 行为一致
const offset_x = e.detail.dx
// 计算当前索引并重置差异
const current_offset_x = offset_x % this.$swiperWidth
const current_offset_i = offset_x / this.$swiperWidth
const current_index = this.$animationFinishIndex + current_offset_i.toInt()
// 计算目标索引及边界检查
let move_to_index = current_index
if (current_offset_x > 0 && move_to_index < this.swiperList.length - 1) {
move_to_index += 1
} else if (current_offset_x < 0 && move_to_index > 0) {
move_to_index -= 1
}
// 计算偏移百分比
const percentage = Math.abs(current_offset_x) / this.$swiperWidth
// 通知更新指示线
this.updateTabIndicator(current_index, move_to_index, percentage)
// 首次可见时初始化数据
this.initSwiperItemData(move_to_index)
},
onSwiperAnimationfinish(e : SwiperAnimationFinishEvent) {
this.setSwiperIndex(e.detail.current, true)
this.$animationFinishIndex = e.detail.current
},
cacheTabItemsSize() {
this.$swiperTabsRect.length = 0
const tabs = this.$refs["swipertab"] as Element[]
tabs.forEach((node) => {
this.$swiperTabsRect.push({
x: node.offsetLeft,
w: node.offsetWidth
} as SwiperTabsItem)
})
},
setSwiperIndex(index : number, updateIndicator : boolean) {
if (this.swiperIndex === index) {
return
}
this.swiperIndex = index
this.initSwiperItemData(index)
if (updateIndicator) {
this.updateTabIndicator(index, index, 1)
}
},
updateTabIndicator(current_index : number, move_to_index : number, percentage : number) {
const current_size = this.$swiperTabsRect[current_index]
const move_to_size = this.$swiperTabsRect[move_to_index]
// 计算指示线 左边距 和 宽度 在移动过程中的线性值
const indicator_line_x = lerpNumber(current_size.x, move_to_size.x, percentage)
const indicator_line_w = lerpNumber(current_size.w, move_to_size.w, percentage)
// 更新指示线
const x = indicator_line_x + indicator_line_w / 2
this.$indicatorNode?.style?.setProperty('transform', `translateX(${x}px) scaleX(${indicator_line_w})`)
// 滚动到水平中心位置
const scroll_x = x - this.$swiperWidth / 2
if(this.$tabScrollView !== null){
this.$tabScrollView!.scrollLeft = scroll_x
}
},
initSwiperItemData(index : number) {
if (!this.swiperList[index].preload) {
this.swiperList[index].preload = true;
(this.$refs["longPage"]! as ComponentPublicInstance[])[index].$callMethod('loadData', null)
}
}
}
}
</script>
<style>
.flex-row {
flex-direction: row;
}
.page {
flex: 1;
}
.search-bar {
padding: 10px;
}
.swiper-list {
height: 100%;
}
.swiper-tabs {
background-color: #ffffff;
flex-direction: column;
}
.swiper-tabs-item {
color: #555;
font-size: 16px;
padding: 12px 25px;
}
.swiper-tabs-item-active {
color: #007AFF;
}
.swiper-tabs-indicator {
width: 1px;
height: 2px;
background-color: #007AFF;
}
.swiper-view {
flex: 1;
}
.swiper-item {
flex: 1;
}
</style>
\ No newline at end of file
this.$tabScrollView!.scrollLeft = scroll_x
}
},
initSwiperItemData(index : number) {
if (!this.swiperList[index].preload) {
this.swiperList[index].preload = true;
(this.$refs["longPage"]! as ComponentPublicInstance[])[index].$callMethod('loadData', null)
}
}
}
}
</script>
<style>
.flex-row {
flex-direction: row;
}
.page {
flex: 1;
}
.search-bar {
padding: 10px;
}
.swiper-list {
height: 100%;
}
.swiper-tabs {
background-color: #ffffff;
flex-direction: column;
}
.swiper-tabs-item {
color: #555;
font-size: 16px;
padding: 12px 25px;
}
.swiper-tabs-item-active {
color: #007AFF;
}
.swiper-tabs-indicator {
width: 1px;
height: 2px;
background-color: #007AFF;
}
.swiper-view {
flex: 1;
}
.swiper-item {
flex: 1;
}
</style>
pages/template/long-list2/long-list2.uvue
浏览文件 @
15096a0a
此差异已折叠。
点击以展开。
pages/template/pull-zoom-image/pull-zoom-image.uvue
浏览文件 @
15096a0a
此差异已折叠。
点击以展开。
pages/template/swiper-vertical-video/swiper-vertical-video.uvue
浏览文件 @
15096a0a
此差异已折叠。
点击以展开。
编辑
预览
Markdown
is supported
0%
请重试
或
添加新附件
.
添加附件
取消
You are about to add
0
people
to the discussion. Proceed with caution.
先完成此消息的编辑!
取消
想要评论请
注册
或
登录