lodash-es#throttle TypeScript Examples
The following examples show how to use
lodash-es#throttle.
You can vote up the ones you like or vote down the ones you don't like,
and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Kplayer.ts From agefans-enhance with MIT License | 6 votes |
jumpToLogTime = throttle(() => {
if (this.isJumped) return
if (this.currentTime < 3) {
this.isJumped = true
const logTime = this.getCurrentTimeLog()
if (logTime && this.plyr.duration - logTime > 10) {
this.message.info(`已自动跳转至历史播放位置 ${parseTime(logTime)}`)
this.currentTime = logTime
}
}
}, 1000)
Example #2
Source File: play.ts From agefans-enhance with MIT License | 5 votes |
function addListener() {
player.on('next', () => {
gotoNextPart()
})
player.on('prev', () => {
gotoPrevPart()
})
player.plyr.once('canplay', () => {
resetVideoHeight()
})
player.on('error', () => {
removeLocal(getActivedom().data('href'))
})
const update = throttle(() => {
updateTime(player.currentTime)
}, 1000)
player.on('timeupdate', () => {
update()
})
player.on('skiperror', (_, duration) => {
if (duration === 0) {
updateTime(0)
} else {
updateTime(player.currentTime + duration)
}
window.location.reload()
})
window.addEventListener('popstate', () => {
const href = location.pathname + location.search
const $dom = $(`[data-href='${href}']`)
if ($dom.length) {
switchPart(href, $dom, false)
} else {
window.location.reload()
}
})
}
Example #3
Source File: history.ts From agefans-enhance with MIT License | 5 votes |
logHis = throttle(his.log.bind(his), 1000)
Example #4
Source File: Kplayer.ts From agefans-enhance with MIT License | 5 votes |
setCurrentTimeLogThrottled = throttle(() => {
this.setCurrentTimeLog()
}, 1000)
Example #5
Source File: Tabs.tsx From atlas with GNU General Public License v3.0 | 4 votes |
Tabs: React.FC<TabsProps> = React.memo(
({ tabs, onSelectTab, initialIndex = -1, selected: paramsSelected, className }) => {
const [_selected, setSelected] = useState(initialIndex)
const selected = paramsSelected ?? _selected
const [isContentOverflown, setIsContentOverflown] = useState(false)
const tabsGroupRef = useRef<HTMLDivElement>(null)
const tabRef = useRef<HTMLDivElement>(null)
const [shadowsVisible, setShadowsVisible] = useState({
left: false,
right: true,
})
useEffect(() => {
const tabsGroup = tabsGroupRef.current
if (!tabsGroup) {
return
}
setIsContentOverflown(tabsGroup.scrollWidth > tabsGroup.clientWidth)
}, [])
useEffect(() => {
const tabsGroup = tabsGroupRef.current
const tab = tabRef.current
if (!tabsGroup || !isContentOverflown || !tab) {
return
}
const { clientWidth, scrollWidth } = tabsGroup
const tabWidth = tab.offsetWidth
const middleTabPosition = clientWidth / 2 - tabWidth / 2
tabsGroup.scrollLeft = tab.offsetLeft - middleTabPosition
const touchHandler = throttle(() => {
setShadowsVisible({
left: tabsGroup.scrollLeft > SCROLL_SHADOW_OFFSET,
right: tabsGroup.scrollLeft < scrollWidth - clientWidth - SCROLL_SHADOW_OFFSET,
})
}, 100)
tabsGroup.addEventListener('touchmove', touchHandler)
tabsGroup.addEventListener('scroll', touchHandler)
return () => {
touchHandler.cancel()
tabsGroup.removeEventListener('touchmove', touchHandler)
tabsGroup.removeEventListener('scroll', touchHandler)
}
}, [isContentOverflown, selected])
const createClickHandler = (idx?: number) => () => {
if (idx !== undefined) {
onSelectTab(idx)
setSelected(idx)
}
}
return (
<TabsWrapper className={className}>
<CSSTransition
in={shadowsVisible.left && isContentOverflown}
timeout={100}
classNames={transitions.names.fade}
unmountOnExit
>
<BackgroundGradient direction="prev" />
</CSSTransition>
<CSSTransition
in={shadowsVisible.right && isContentOverflown}
timeout={100}
classNames={transitions.names.fade}
unmountOnExit
>
<BackgroundGradient direction="next" />
</CSSTransition>
<TabsGroup ref={tabsGroupRef}>
{tabs.map((tab, idx) => (
<Tab
onClick={createClickHandler(idx)}
key={`${tab.name}-${idx}`}
selected={selected === idx}
ref={selected === idx ? tabRef : null}
>
<span data-badge={tab.badgeNumber}>
{tab.name}
{tab.pillText && <StyledPill size="small" label={tab.pillText} />}
</span>
</Tab>
))}
</TabsGroup>
</TabsWrapper>
)
}
)
Example #6
Source File: EmbeddedView.tsx From atlas with GNU General Public License v3.0 | 4 votes |
EmbeddedView: React.FC = () => {
useRedirectMigratedContent({ type: 'embedded-video' })
const { id } = useParams()
const { loading, video, error } = useVideo(id ?? '', {
onError: (error) => SentryLogger.error('Failed to load video data', 'VideoView', error),
})
const { addVideoView } = useAddVideoView()
const { url: mediaUrl, isLoadingAsset: isMediaLoading } = useAsset(video?.media)
const { url: thumbnailUrl, isLoadingAsset: isThumbnailLoading } = useAsset(video?.thumbnailPhoto)
const { startTimestamp } = useVideoStartTimestamp(video?.duration)
const channelId = video?.channel?.id
const videoId = video?.id
const categoryId = video?.category?.id
useEffect(() => {
if (!videoId || !channelId) {
return
}
addVideoView({
variables: {
videoId,
channelId,
categoryId,
},
}).catch((error) => {
SentryLogger.error('Failed to increase video views', 'VideoView', error)
})
}, [addVideoView, videoId, channelId, categoryId])
const handleVideoEnded = () => {
if (window.top) {
window.top.postMessage('atlas_video_ended', '*')
}
}
const throttledSendTimeUpdate = useRef(
throttle((time: number) => {
if (window.top) {
window.top.postMessage(`atlas_video_progress:${time}`, '*')
}
}, 1000)
)
const handleTimeUpdated = (time: number) => {
if (!video?.duration) {
return
}
const progress = time / video.duration
// make sure progress is in 0..1 range
const normalizedProgress = Math.min(Math.max(0, progress), 1)
throttledSendTimeUpdate.current(normalizedProgress)
}
if (error) {
return <ViewErrorFallback />
}
if (!loading && !video) {
return (
<NotFoundVideoContainer>
<EmptyFallback
title="Video not found"
button={
<Button variant="secondary" size="large" to={absoluteRoutes.viewer.index()}>
Go back to home page
</Button>
}
/>
</NotFoundVideoContainer>
)
}
return (
<>
<EmbeddedGlobalStyles />
<Container>
{!isMediaLoading && !isThumbnailLoading && video ? (
<VideoPlayer
isVideoPending={!video?.media?.isAccepted}
channelId={video.channel?.id}
videoId={video.id}
src={mediaUrl}
posterUrl={thumbnailUrl}
fill
startTime={startTimestamp}
onEnd={handleVideoEnded}
onTimeUpdated={handleTimeUpdated}
isEmbedded
/>
) : (
<PlayerSkeletonLoader />
)}
</Container>
</>
)
}
Example #7
Source File: VideoView.tsx From atlas with GNU General Public License v3.0 | 4 votes |
VideoView: React.FC = () => {
useRedirectMigratedContent({ type: 'video' })
const { id } = useParams()
const { openNftPutOnSale, cancelNftSale, openNftAcceptBid, openNftChangePrice, openNftPurchase, openNftSettlement } =
useNftActions()
const { withdrawBid } = useNftTransactions()
const { loading, video, error } = useVideo(id ?? '', {
onError: (error) => SentryLogger.error('Failed to load video data', 'VideoView', error),
})
const nftWidgetProps = useNftWidget(id)
const mdMatch = useMediaMatch('md')
const { addVideoView } = useAddVideoView()
const {
watchedVideos,
cinematicView,
actions: { updateWatchedVideos },
} = usePersonalDataStore((state) => state)
const category = useCategoryMatch(video?.category?.id)
const { anyOverlaysOpen } = useOverlayManager()
const { ref: playerRef, inView: isPlayerInView } = useInView()
const pausePlayNext = anyOverlaysOpen || !isPlayerInView
const { url: mediaUrl, isLoadingAsset: isMediaLoading } = useAsset(video?.media)
const { url: thumbnailUrl } = useAsset(video?.thumbnailPhoto)
const videoMetaTags = useMemo(() => {
if (!video || !thumbnailUrl) return {}
return generateVideoMetaTags(video, thumbnailUrl)
}, [video, thumbnailUrl])
const headTags = useHeadTags(video?.title, videoMetaTags)
const { startTimestamp, setStartTimestamp } = useVideoStartTimestamp(video?.duration)
// Restore an interrupted video state
useEffect(() => {
if (startTimestamp != null || !video) {
return
}
const currentVideo = watchedVideos.find((v) => v.id === video?.id)
setStartTimestamp(currentVideo?.__typename === 'INTERRUPTED' ? currentVideo.timestamp : 0)
}, [watchedVideos, startTimestamp, video, setStartTimestamp])
const channelId = video?.channel?.id
const channelName = video?.channel?.title
const videoId = video?.id
const categoryId = video?.category?.id
useEffect(() => {
if (!videoId || !channelId) {
return
}
addVideoView({
variables: {
videoId,
channelId,
categoryId,
},
}).catch((error) => {
SentryLogger.error('Failed to increase video views', 'VideoView', error)
})
}, [addVideoView, videoId, channelId, categoryId])
// Save the video timestamp
// disabling eslint for this line since debounce is an external fn and eslint can't figure out its args, so it will complain.
// eslint-disable-next-line react-hooks/exhaustive-deps
const handleTimeUpdate = useCallback(
throttle((time) => {
if (video?.id) {
updateWatchedVideos('INTERRUPTED', video.id, time)
}
}, 5000),
[video?.id]
)
const handleVideoEnd = useCallback(() => {
if (video?.id) {
handleTimeUpdate.cancel()
updateWatchedVideos('COMPLETED', video?.id)
}
}, [video?.id, handleTimeUpdate, updateWatchedVideos])
// use Media Session API to provide rich metadata to the browser
useEffect(() => {
const supported = 'mediaSession' in navigator
if (!supported || !video) {
return
}
const artwork: MediaImage[] = thumbnailUrl ? [{ src: thumbnailUrl, type: 'image/webp', sizes: '640x360' }] : []
navigator.mediaSession.metadata = new MediaMetadata({
title: video.title || '',
artist: video.channel.title || '',
album: '',
artwork: artwork,
})
return () => {
navigator.mediaSession.metadata = null
}
}, [thumbnailUrl, video])
if (error) {
return <ViewErrorFallback />
}
if (!loading && !video) {
return (
<NotFoundVideoContainer>
<EmptyFallback
title="Video not found"
button={
<Button variant="secondary" size="large" to={absoluteRoutes.viewer.index()}>
Go back to home page
</Button>
}
/>
</NotFoundVideoContainer>
)
}
const isCinematic = cinematicView || !mdMatch
const sideItems = (
<GridItem colSpan={{ xxs: 12, md: 4 }}>
{!!nftWidgetProps && (
<NftWidget
{...nftWidgetProps}
onNftPutOnSale={() => id && openNftPutOnSale(id)}
onNftCancelSale={() => id && nftWidgetProps.saleType && cancelNftSale(id, nftWidgetProps.saleType)}
onNftAcceptBid={() => id && openNftAcceptBid(id)}
onNftChangePrice={() => id && openNftChangePrice(id)}
onNftPurchase={() => id && openNftPurchase(id)}
onNftSettlement={() => id && openNftSettlement(id)}
onNftBuyNow={() => id && openNftPurchase(id, { fixedPrice: true })}
onWithdrawBid={() => id && withdrawBid(id)}
/>
)}
<MoreVideos channelId={channelId} channelName={channelName} videoId={id} type="channel" />
<MoreVideos categoryId={category?.id} categoryName={category?.name} videoId={id} type="category" />
</GridItem>
)
const detailsItems = (
<>
{headTags}
<TitleContainer>
{video ? (
<TitleText variant={mdMatch ? 'h600' : 'h400'}>{video.title}</TitleText>
) : (
<SkeletonLoader height={mdMatch ? 56 : 32} width={400} />
)}
<Meta variant={mdMatch ? 't300' : 't100'} secondary>
{video ? (
formatVideoViewsAndDate(video.views || null, video.createdAt, { fullViews: true })
) : (
<SkeletonLoader height={24} width={200} />
)}
</Meta>
</TitleContainer>
<ChannelContainer>
<ChannelLink followButton id={channelId} textVariant="h300" avatarSize="small" />
</ChannelContainer>
<VideoDetails video={video} category={category} />
</>
)
return (
<>
<PlayerGridWrapper cinematicView={isCinematic}>
<PlayerWrapper cinematicView={isCinematic}>
<PlayerGridItem colSpan={{ xxs: 12, md: cinematicView ? 12 : 8 }}>
<PlayerContainer className={transitions.names.slide} cinematicView={cinematicView}>
{!isMediaLoading && video ? (
<VideoPlayer
isVideoPending={!video?.media?.isAccepted}
channelId={video?.channel?.id}
videoId={video?.id}
autoplay
src={mediaUrl}
onEnd={handleVideoEnd}
onTimeUpdated={handleTimeUpdate}
startTime={startTimestamp}
isPlayNextDisabled={pausePlayNext}
ref={playerRef}
/>
) : (
<PlayerSkeletonLoader />
)}
</PlayerContainer>
{!isCinematic && detailsItems}
</PlayerGridItem>
{!isCinematic && sideItems}
</PlayerWrapper>
</PlayerGridWrapper>
<LimitedWidthContainer>
{isCinematic && (
<LayoutGrid>
<GridItem className={transitions.names.slide} colSpan={{ xxs: 12, md: cinematicView ? 8 : 12 }}>
{detailsItems}
</GridItem>
{sideItems}
</LayoutGrid>
)}
<StyledCallToActionWrapper>
{['popular', 'new', 'discover'].map((item, idx) => (
<CallToActionButton key={`cta-${idx}`} {...CTA_MAP[item]} />
))}
</StyledCallToActionWrapper>
</LimitedWidthContainer>
</>
)
}