修改页面级加载 和 图片加载

v2
king 1 year ago
parent 1eb468a699
commit 20564f42fb
  1. 12
      src/components/image/image.module.scss
  2. 37
      src/components/image/image.tsx
  3. 22
      src/components/spinner/index.tsx
  4. 12
      src/components/spinner/style.scss
  5. 6
      src/pages/business/videoInfo/videoInfo.scss
  6. 22
      src/pages/business/videoInfo/videoInfo.tsx
  7. 4
      src/pages/home/components/feature_recommended.tsx
  8. 2
      src/pages/home/home.module.scss
  9. 7
      src/pages/preview/health/health.tsx
  10. 19
      src/pages/preview/illness/sort/sort.tsx
  11. 15
      src/pages/preview/profession/profession.tsx

@ -0,0 +1,12 @@
.imgBox {
position: relative;
}
.imgError {
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
margin: auto;
}

@ -1,7 +1,8 @@
import {FC, useEffect, useState} from "react";
import {Image, ImageProps, View} from "@tarojs/components";
import {AtActivityIndicator} from "taro-ui";
import shard from '@/static/img/shard.png'
import styles from './image.module.scss'
import Taro from "@tarojs/taro";
interface Props extends ImageProps {
width: number
@ -13,14 +14,12 @@ const Img: FC<Props> = ({src, mode = 'aspectFill', width, height, fallback = sha
const [isError, setIsError] = useState(false)
const [loading, setLoading] = useState(true)
const imgAnimation = Taro.createAnimation({duration: 0}).opacity(0).step()
const [animationData, setAnimationData] = useState<TaroGeneral.IAnyObject>(imgAnimation.export())
useEffect(() => {
if (!src) {
setIsError(true)
setLoading(false)
} else {
setIsError(false)
setLoading(false)
}
setIsError(!src)
setLoading(!!src)
}, [src])
// 图片加载失败
@ -32,25 +31,37 @@ const Img: FC<Props> = ({src, mode = 'aspectFill', width, height, fallback = sha
function onLoadHandler() {
setLoading(false)
setIsError(false)
imgAnimation.opacity(1).step({duration: 200})
setAnimationData(imgAnimation.export())
}
return (
<View style={{width: `${width}rpx`, height: `${height}rpx`, backgroundColor: '#eee'}}>
<View
style={{width: `${width}rpx`, height: `${height}rpx`, backgroundColor: '#F8F8F8'}}
className={`${props.className} ${styles.imgBox}`}>
{!isError &&
<View animation={animationData}>
<Image
{...props}
src={src}
mode={mode}
lazyLoad
fadeIn
defaultSource={fallback}
style={{width: `${width}rpx`, height: `${height}rpx`}}
onError={onErrorHandler}
onLoad={onLoadHandler}>
</Image>
onLoad={onLoadHandler}/>
</View>
}
{loading && <AtActivityIndicator mode="center"/>}
{
isError && !loading &&
<Image mode={'aspectFill'} src={fallback} style={{width: `${width}rpx`, height: `${height}rpx`}}/>
<Image
className={styles.imgError}
mode='aspectFit'
src={fallback}
lazyLoad
fadeIn
style={{width: `${width * .7}rpx`, height: `${height * .7}rpx`, margin: 'auto'}}/>
}
</View>
)

@ -12,8 +12,9 @@ type Status =
| 'completed'
interface Props {
enable?: boolean
overlay?: boolean
enable?: boolean // 控制显现
overlay?: boolean // 页面覆盖
block?: boolean // 块级
}
interface State {
@ -49,8 +50,8 @@ function createController(setState: StateSetter): Controller {
}
// 清空动画
background.export()
rotation.export()
background.step().export()
rotation.step().export()
// 通知 UI 刷新
if (notify) {
@ -80,7 +81,6 @@ function createController(setState: StateSetter): Controller {
const onFinish = (opacity: number, nextStatus: Status) => {
const lockStatus = status
setTimeout(() => {
if (lockStatus === status) {
background.backgroundColor(`rgba(255,255,255,${opacity})`).step({duration: 0})
if (nextStatus === 'dismissed') {
@ -89,14 +89,12 @@ function createController(setState: StateSetter): Controller {
status = nextStatus
notifyListener()
}
}, 600)
}
const setStatus = (newStatus: Status, opacity: number) => {
if (status !== newStatus) {
status = newStatus
setAnimation(opacity)
if (status === 'reverse') {
onFinish(0, 'dismissed')
} else if (status === 'forward') {
@ -140,10 +138,9 @@ export default class Spin extends Component<Props, State> {
this.setState((s) => ({...s, ...state}))
}
componentDidMount(): void {
console.log(this.props.enable)
this.controller.setTick(this.props.enable)
}
// componentDidMount(): void {
// this.controller.setTick(this.props.enable)
// }
componentDidUpdate(): void {
this.controller.setTick(this.props.enable)
@ -161,7 +158,8 @@ export default class Spin extends Component<Props, State> {
render(): ReactNode {
return (
<View className={`spinner-wrapper ${this.state.status} ${this.props.overlay ? 'is-fixed' : ''}`}>
<View
className={`spinner-wrapper ${this.state.status} ${this.props.overlay && 'is-fixed'} ${this.props.block && 'is-block'}`}>
<View className={`spinner ${this.state.status}`}>
<Image className="spinner-icon" src={indicator}/>
</View>

@ -1,8 +1,17 @@
.spinner-wrapper {
background-color: rgba( #fff, 1.0);
transition: background-color 1200ms ease-out;
&.is-block {
width: auto;
position: absolute;
z-index: 99999;
left: 50%;
top: 50%;
transform: translate(-50%, -50%);
}
&.is-fixed {
background-color: rgba(#fff, 1.0);
z-index: 99999;
position: fixed;
width: 100%;
@ -36,6 +45,7 @@
flex-direction: column;
justify-content: center;
align-items: center;
width: 100%;
}
.spinner {

@ -7,11 +7,7 @@
height: 500rpx;
}
.image {
width: 100%;
height: 100%;
display: block;
}
.header {
margin-bottom: 10px;

@ -1,4 +1,4 @@
import {Image, Text, View} from "@tarojs/components";
import {Text, View} from "@tarojs/components";
import {FC, useCallback, useEffect, useState} from "react";
import {CourseDepData, curriculum} from "@/api";
import './videoInfo.scss'
@ -9,6 +9,8 @@ import eventsIndex from "@/hooks/eventsIndex";
import {formatMinute} from "@/utils/time";
import videoEvents from "@/hooks/videoEvents";
import unique_ident from "@/hooks/unique_ident";
import Spin from "@/components/spinner";
import Img from "@/components/image/image";
const VideoInfo: FC = () => {
const {id, depId} = Taro.getCurrentInstance()?.router?.params as any
@ -16,15 +18,16 @@ const VideoInfo: FC = () => {
const [playId, setPlayId] = useState<number | null>(null)
const [preview, setPreview] = useState(false) // 预览
const [playing, setPlaying] = useState(false) // 学习中
const [enable, setEnable] = useState(true)
const getData = useCallback(async (playing: boolean) => {
try {
const res = await curriculum.courseDep(id, depId)
if (res) {
setData(res)
}
if (playId != null) { // 用于自动播放 判断当前课程是否完成
currentVideo(res, playing)
res && setData(res)
playId != null && currentVideo(res, playing) // 用于自动播放 判断当前课程是否完成
} catch (e) {
}
setEnable(false)
}, [playing, playId])
const curEnd = (test?: boolean) => {
@ -73,9 +76,7 @@ const VideoInfo: FC = () => {
}
}, [playId, data, preview])
/**
*
*/
/** 判断当前课程是否完成 */
const currentVideo = useCallback((data: CourseDepData, playing: boolean) => {
const courseHourRecordsFinish = data?.learn_hour_records.find(d => d.id === playId)?.courseHourRecordsFinish
if (typeof courseHourRecordsFinish === 'number') {
@ -106,12 +107,13 @@ const VideoInfo: FC = () => {
})
return (
<>
<Spin enable={enable} overlay/>
<View className='content'>
<View className='content-video'>
{
playId
? <Course id={playId} courseId={id} curEnd={curEnd} preview={preview}/>
: <Image src={data?.course.thumb || ''} className='image' mode='aspectFill'/>
: <Img width={750} height={500} src={data?.course.thumb || ''} mode='aspectFill'/>
}
</View>

@ -150,8 +150,8 @@ const FeatureRecommended: FC<Props> = (props) => {
}
return (
<View className={styles.feature} style={{height: '263px'}}>
<Swiper nextMargin='30px' style={{height: '263px'}}>
<View className={styles.feature}>
<Swiper nextMargin='30px' style={{height: '225px'}}>
{
data.map(d => <SwiperItem key={d.url}>
<Image

@ -51,9 +51,9 @@
}
.adware {
width: 100%;
border-radius: 16rpx;
overflow: hidden;
background: #eee;
margin-bottom: 40rpx;
}

@ -5,16 +5,22 @@ import Taro, {useReachBottom} from "@tarojs/taro";
import styles from './health.module.scss'
import play from '@/static/img/play.png'
import Empty from "@/components/empty/empty";
import Spin from "@/components/spinner";
const Health: FC = () => {
const [page, setPage] = useState(1)
const [data, setData] = useState<Health[]>([])
const [total, setTotal] = useState(0)
const [enable, setEnable] = useState(true)
async function getData(page: number) {
try {
const res = await HomeApi.health(page, 10)
setData(res.data)
setTotal(res.total)
} catch (e) {
}
setEnable(false)
}
useReachBottom(() => {
@ -32,6 +38,7 @@ const Health: FC = () => {
return (
<ScrollView>
<Spin enable={enable} overlay/>
<View className={styles.container}>
{
data.length > 0

@ -7,6 +7,7 @@ import Tabs, {TabList} from "@/components/tabs/tabs";
import {illnessApi} from "@/api/illness";
import Empty from "@/components/empty/empty";
import leftArrow from "@/static/img/leftArrow.png"
import Spin from "@/components/spinner";
const prefix = 'SORT'
const Sort: FC = () => {
@ -14,16 +15,24 @@ const Sort: FC = () => {
const [firstId, setFirstId] = useState<number | undefined>(undefined) // 一级分类
const [secondId, setSecondId] = useState<number | undefined>(undefined) // 二级分类
const [list, setList] = useState<any[]>([])
const [enable, setEnable] = useState(true)
const [loading, setLoading] = useState(false)
const globalData = Taro.getApp().globalData
const menu = Taro.getMenuButtonBoundingClientRect()
async function getData() {
try {
const res = await HomeApi.category(3)
setData(res)
if (res.length) {
setFirstId(res[0].id)
setSecondId(res[0]?.resource_category?.[0].id)
}
} catch (e) {
setLoading(false)
}
setEnable(false)
}
function jump(id: number) {
@ -54,17 +63,16 @@ const Sort: FC = () => {
useEffect(() => {
if (secondId) {
Taro.showLoading({title: '加载中'})
setLoading(true)
illnessApi.list(secondId, 1, 100).then(res => {
setList(res.list)
}).finally(() => {
Taro.hideLoading()
}).catch(() => {
setList([])
})
} else {
setList([])
}
setLoading(false)
}, [secondId])
function swiperChange(e) {
@ -76,6 +84,7 @@ const Sort: FC = () => {
return (
<View>
<Spin enable={enable} overlay/>
<View style={headerStyles}>
<Image src={leftArrow}
mode='widthFix'
@ -118,11 +127,15 @@ const Sort: FC = () => {
scrollY
className={styles.tree}
scrollWithAnimation>
{
loading ? <Spin enable={loading}/> : <>
{
list.length ?
list.map(d => <View className={styles.list} onClick={() => jump(d.id)}>{d.name}</View>)
: <Empty name='暂无数据'/>
}
</>
}
</ScrollView>
</SwiperItem>)
}

@ -5,6 +5,7 @@ import Tabs, {OnChangOpt, TabList} from "@/components/tabs/tabs";
import Empty from "@/components/empty/empty";
import Taro from "@tarojs/taro";
import styles from './profession.module.scss'
import Spin from "@/components/spinner";
interface KillData {
data: Kill[]
@ -16,6 +17,8 @@ const Profession = () => {
const [tabs, setTabs] = useState<TabList[]>([])
const [categoryId, setCategoryId] = useState<number | null>(null)
const [data, setData] = useState<Map<number, KillData>>(new Map)
const [enable, setEnable] = useState(true)
const [loading, setLoading] = useState(false)
/**
* more
@ -32,9 +35,7 @@ const Profession = () => {
}
try {
if (!data.has(categoryId)) {
Taro.showLoading()
}
setLoading(true)
const res = await HomeApi.skillList(categoryId!, page, 10)
const dataList = res.data.reduce((pre, cur) => {
const index = pre.findIndex(d => d.id === cur.id)
@ -54,7 +55,7 @@ const Profession = () => {
setData(oldData)
} catch (e) {
}
Taro.hideLoading()
setLoading(false)
}
useEffect(() => {
@ -62,10 +63,14 @@ const Profession = () => {
}, [categoryId])
async function getCategory() {
try {
const res = await HomeApi.skillCategory()
const newTabs = res.map<TabList>(d => ({title: d.name, value: d.id}))
setTabs(newTabs)
setCategoryId(newTabs[0].value as number)
} catch (e) {
}
setEnable(false)
}
function tabsChange(tab: OnChangOpt) {
@ -101,6 +106,7 @@ const Profession = () => {
</View>
)
}
<Spin enable={loading}/>
</ScrollView>
)
}
@ -108,6 +114,7 @@ const Profession = () => {
return (
<>
<Spin enable={enable} overlay/>
<View className='bg-white'>
<Tabs tabList={tabs} onChange={tabsChange} current={categoryId!}/>
</View>

Loading…
Cancel
Save