Skip to content

Commit 9725552

Browse files
committed
add app-bar-course
1 parent 1b9f54b commit 9725552

File tree

6 files changed

+353
-55
lines changed

6 files changed

+353
-55
lines changed

miniprogram/app-bar/index.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
{
22
"component": true,
3-
"usingComponents": {}
3+
"usingComponents": {
4+
"app-bar-course": "../components/app-bar-course"
5+
}
46
}

miniprogram/app-bar/index.wxml

Lines changed: 3 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,3 @@
1-
<!-- components/app-bar/index.wxml -->
2-
<vertical-drag-gesture-handler worklet:ongesture="handleVerticalDrag">
3-
<view class="expand-container" wx:if="{{showAppbar}}">
4-
<!-- 放大模式:nav-bar -->
5-
<view class="nav-bar column">
6-
<view style="height: {{statusBarHeight}}px;" />
7-
<view style="flex: 1;" class="column-main-center">
8-
<image
9-
class="icon--back"
10-
mode="aspectFill"
11-
src="/assets/arrow-down.png"
12-
bind:tap="close"
13-
/>
14-
</view>
15-
</view>
16-
17-
<!-- 跟着手势变化 -->
18-
<view class="cover-area" style="height: {{maxCoverSize}}px;">
19-
<view class="row " bind:tap="expand">
20-
<image class="cover" mode="aspectFill" src="{{musicCover}}" />
21-
<view class="title-wrap row-between">
22-
<view class="title column">
23-
<text overflow="ellipsis" max-lines="1">Skyline 渲染框架入门与实践</text>
24-
<text class="name" overflow="ellipsis" max-lines="1">小程序技术专员 - binnie</text>
25-
</view>
26-
<view class="row">
27-
<image class="icon" style="margin-right: 24px;" src="/assets/play.png" />
28-
<image class="icon" src="/assets/next.png" />
29-
</view>
30-
</view>
31-
</view>
32-
</view>
33-
34-
<!-- 放大模式:小字 -->
35-
<view class="row-between">
36-
<text>微信学堂</text>
37-
<text>88 人在学</text>
38-
</view>
39-
40-
<!-- 放大模式:标题 -->
41-
<view class="music-title column" style="margin-top: 50px;">
42-
<text>Skyline 渲染框架入门与实践</text>
43-
<text class="name">小程序技术专员 - binnie</text>
44-
</view>
45-
46-
<!-- 底部操作栏 -->
47-
<view class="footer row-between" style="margin-top: 50px;">
48-
<image class="icon" src="/assets/next.png" style="transform: rotate(180deg);" />
49-
<image class="icon" src="/assets/play.png" />
50-
<image class="icon" src="/assets/next.png" />
51-
</view>
52-
</view>
53-
</vertical-drag-gesture-handler>
54-
1+
<view wx:if="{{showAppbar}}">
2+
<app-bar-course></app-bar-course>
3+
</view>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"component": true,
3+
"usingComponents": {}
4+
}
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
.expand-container {
2+
position: absolute;
3+
top: 0;
4+
right: 0;
5+
bottom: 0;
6+
left: 0;
7+
padding: 0 24px;
8+
padding-bottom: env(safe-area-inset-bottom);
9+
pointer-events: auto;
10+
overflow: hidden;
11+
background-color: #e9f8f7;
12+
color: #7e8081;
13+
}
14+
15+
.hide {
16+
display: none;
17+
}
18+
19+
.nav-bar {
20+
overflow: hidden;
21+
box-sizing: border-box;
22+
}
23+
24+
.icon--back {
25+
width: 30px;
26+
height: 30px;
27+
}
28+
29+
.title {
30+
margin-left: 10px;
31+
min-width: 160px;
32+
flex: 1;
33+
}
34+
.title .name {
35+
margin-top: 4px;
36+
font-size: 12px;
37+
}
38+
39+
.title-wrap {
40+
flex: 1;
41+
min-width: 240px;
42+
}
43+
44+
.footer {
45+
padding: 0 24px;
46+
}
47+
48+
.footer .icon {
49+
width: 40px;
50+
height: 40px;
51+
}
52+
53+
.expand-cover {
54+
width: 100%;
55+
height: 100%;
56+
}
57+
58+
.music-title {
59+
margin-top: 48px;
60+
font-size: 20px;
61+
font-weight: bold;
62+
color: #07c160;
63+
}
64+
.music-title .name {
65+
margin-top: 12px;
66+
font-size: 14px;
67+
font-weight: 200;
68+
color: #b1b2b3;
69+
}
70+
71+
.cover-area {
72+
margin: 8px 0;
73+
width: 100%;
74+
aspect-ratio: 1 / 1;
75+
overflow: hidden;
76+
}
77+
78+
.cover {
79+
/* aspect-ratio: 1 / 1; */
80+
flex-shrink: 0;
81+
}
82+
83+
.icon {
84+
width: 18px;
85+
height: 18px;
86+
}
87+
88+
.row {
89+
display: flex;
90+
flex-direction: row;
91+
align-items: center;
92+
}
93+
94+
.row-between {
95+
display: flex;
96+
flex-direction: row;
97+
justify-content: space-between;
98+
align-items: center;
99+
}
100+
101+
.column {
102+
display: flex;
103+
flex-direction: column;
104+
}
105+
106+
.column-main-center {
107+
display: flex;
108+
flex-direction: column;
109+
justify-content: center;
110+
}
111+
112+
.column-cross-center {
113+
display: flex;
114+
flex-direction: column;
115+
align-items: center;
116+
}
117+
118+
.center {
119+
display: flex;
120+
align-items: center;
121+
justify-content: center;
122+
}
123+
124+
.circle {
125+
width: 100%;
126+
height: 100%;
127+
overflow: hidden;
128+
border-radius: 50%;
129+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
// components/app-bar/index.js
2+
3+
const { shared, timing, Easing } = wx.worklet
4+
5+
export const GestureState = {
6+
POSSIBLE: 0,
7+
BEGIN: 1,
8+
ACTIVE: 2,
9+
END: 3,
10+
CANCELLED: 4,
11+
}
12+
13+
export const lerp = function (begin, end, t) {
14+
'worklet'
15+
return begin + (end - begin) * t
16+
}
17+
18+
export const clamp = function (cur, lowerBound, upperBound) {
19+
'worklet'
20+
if (cur > upperBound) return upperBound
21+
if (cur < lowerBound) return lowerBound
22+
return cur
23+
}
24+
25+
const systemInfo = wx.getSystemInfoSync()
26+
const { statusBarHeight, screenHeight, screenWidth, safeArea } = systemInfo
27+
console.info('@@@ systemInfo', systemInfo)
28+
Component({
29+
properties: {
30+
31+
},
32+
33+
data: {
34+
showAppbar: false,
35+
maxCoverSize: 0,
36+
statusBarHeight: 0,
37+
musicCover: 'https://res.wx.qq.com/op_res/Nu9XXzXcXnD1j5EgWQ2ElxNcl1yMvnKypRo4MTbjOv7FC3saigGoOBTZibyESC7EXaClnPYhB6pvfb-IRmso6g'
38+
},
39+
40+
lifetimes: {
41+
attached() {
42+
const progress = shared(0)
43+
const initCoverSize = 60 // 初始图片大小
44+
const pagePadding = 24
45+
const maxCoverSize = screenWidth - 2 * pagePadding
46+
const safeAreaInsetBottom = screenHeight - safeArea.bottom
47+
const isIOS = systemInfo.system.indexOf('iOS') >= 0
48+
this.setData({ statusBarHeight, maxCoverSize })
49+
console.log('attached: ', statusBarHeight, maxCoverSize)
50+
51+
this.applyAnimatedStyle('.cover', () => {
52+
'worklet'
53+
const height = initCoverSize + (maxCoverSize - initCoverSize) * progress.value
54+
console.log('height: ', maxCoverSize, initCoverSize, progress.value)
55+
return {
56+
width: `${height}px`,
57+
height:`${height}px`,
58+
}
59+
})
60+
61+
this.applyAnimatedStyle('.expand-container', () => {
62+
'worklet'
63+
console.log('expand-container: ', maxCoverSize, initCoverSize, progress.value)
64+
const t = progress.value
65+
const maxRadius = 30
66+
const radius = isIOS ? maxRadius * t : 0
67+
const initBarHeight = initCoverSize + 8 * 2 + safeAreaInsetBottom
68+
return {
69+
top: `${(screenHeight - initBarHeight) * (1 - t)}px`,
70+
borderRadius: `${radius}px ${radius}px 0px 0px`
71+
}
72+
})
73+
74+
this.applyAnimatedStyle('.title-wrap', () => {
75+
'worklet'
76+
console.log('title-wrap: ', maxCoverSize, initCoverSize, progress.value)
77+
return {
78+
opacity: 1 - progress.value
79+
}
80+
})
81+
82+
const navBarHeight = statusBarHeight + (isIOS ? 40 : 44)
83+
this.applyAnimatedStyle('.nav-bar', () => {
84+
'worklet'
85+
console.log('nav-bar: ', maxCoverSize, initCoverSize, progress.value)
86+
const t = progress.value
87+
const threshold = 0.8
88+
const opacity = t < threshold ? 0 : (t - threshold) / (1 - threshold)
89+
90+
return {
91+
opacity,
92+
height: `${navBarHeight * progress.value}px`
93+
}
94+
})
95+
96+
this.progress = progress
97+
}
98+
},
99+
100+
methods: {
101+
close() {
102+
this.progress.value = timing(0, {
103+
duration: 250,
104+
easing: Easing.ease
105+
})
106+
},
107+
108+
expand() {
109+
this.progress.value = timing(1, {
110+
duration: 250,
111+
easing: Easing.ease
112+
})
113+
},
114+
115+
handleDragUpdate(delta) {
116+
'worklet'
117+
const curValue = this.progress.value
118+
const newVal = curValue - delta
119+
this.progress.value = clamp(newVal, 0.0, 1.0)
120+
},
121+
122+
handleDragEnd(velocity) {
123+
'worklet'
124+
const t = this.progress.value
125+
let animateForward = false
126+
if (Math.abs(velocity) >= 1) {
127+
animateForward = velocity <= 0
128+
} else {
129+
animateForward = t > 0.7
130+
}
131+
const animationCurve = Easing.out(Easing.ease)
132+
if (animateForward) {
133+
this.progress.value = timing(
134+
1.0, {
135+
duration: 200,
136+
easing: animationCurve,
137+
})
138+
} else {
139+
this.progress.value = timing(
140+
0.0, {
141+
duration: 250,
142+
easing: animationCurve,
143+
})
144+
}
145+
},
146+
147+
handleVerticalDrag(evt) {
148+
'worklet'
149+
if (evt.state === GestureState.ACTIVE) {
150+
const delta = evt.deltaY / screenHeight
151+
this.handleDragUpdate(delta)
152+
} else if (evt.state === GestureState.END) {
153+
const velocity = evt.velocityY / screenHeight
154+
this.handleDragEnd(velocity)
155+
} else if (evt.state === GestureState.CANCELLED) {
156+
this.handleDragEnd(0.0)
157+
}
158+
},
159+
},
160+
161+
})

0 commit comments

Comments
 (0)