Я пытаюсь использовать API императивной маршрутизации Next.js в обработчике прокрутки для навигации по страницам. Когда я прикрепляюсь к окну, оно почти идеально, за небольшим исключением, что при сбросе положения прокрутки появляется цветная «вспышка» (необходимо продолжать прокручивать вверх от нижней части нового маршрута, если перейти к верхней части предыдущей страницы, и переход цвета должен быть бесшовным, что кажется невозможным из-за миллисекунд браузера с scrollTop в 0). Код:
export default class ScrollOMatic extends Component {
constructor(props) {
super(props)
this.state = {
prevRoute: '',
nextRoute: '',
isEndOfScroll: false
}
binder(this, ['handleScroll'])
}
componentWillMount() {
if (typeof window !== 'undefined') {
console.log(window)
}
}
componentDidMount() {
window.addEventListener('scroll', this.handleScroll)
const {
prevRoute,
nextRoute
} = this.props.routeData
this.setState({
prevRoute,
nextRoute
})
Router.prefetch('us')
Router.prefetch('work')
Router.prefetch('services')
Router.prefetch('converse')
}
componentWillUnmount() {
window.removeEventListener('scroll', this.handleScroll)
}
handleScroll(e) {
e.preventDefault()
let {
scrollTop,
scrollHeight
} = e.srcElement.scrollingElement
scrollHeight = scrollHeight / 2
const scrollTiplier = scrollTop / scrollHeight
if (scrollTop === 0) {
Router.push(this.state.prevRoute)
window.scrollTo(0, scrollHeight - 1, {
duration: 0
})
}
if (scrollTiplier === 1) {
Router.push(this.state.nextRoute)
window.scrollTo(0, 1, {
duration: 0
})
}
}
render() {
return (
<div className = 'scroll-o-matic' >{ this.props.children }</div>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
Итак, я использую контейнер с собственным поведением прокрутки, вдохновленным реакцией горизонтальной прокрутки, которая кажется многообещающей. Но с этим новым кодом происходит странная вещь. Новый маршрут на мгновение отображается на стороне клиента, и страница обновляется по мере получения нового маршрута с сервера. См. gif и код:
// inspired by react-horizontal-scroll
import { Motion, spring, presets } from 'react-motion'
import raf from 'raf'
import Router from 'next/router'
import { fadeColor, binder } from '../../lib/_utils'
import { setScrollState } from '../../lib/_navRules'
export default class ScrollOMatic extends Component {
constructor (props) {
super(props)
this.state = {
prevRoute: '',
nextRoute: '',
animValues: 0
}
binder(this, ['handleWheel', 'resetMin', 'resetMax', 'navigator', 'canIscroll', 'getLayoutData'])
}
componentDidMount () {
const { prevRoute, nextRoute } = this.props.routeData
this.setState({
prevRoute,
nextRoute
})
const prevRouteName = prevRoute === '/' ? 'index' : prevRoute.replace('/', '')
const nextRouteName = prevRoute === '/' ? 'index' : nextRoute.replace('/', '')
Router.prefetch(prevRouteName)
Router.prefetch(nextRouteName)
}
componentWillReceiveProps (nextProps) {
if (this.props.children !== nextProps.children) { this.resetMin() }
}
shouldComponentUpdate (nextProps, nextState) {
if (true &&
this.calculate.timer !== void 0 &&
this.props.children === nextProps.children &&
this.state.animValues === nextState.animValues) {
return false
}
if (true &&
this.props.children === nextProps.children &&
this.canIscroll() === false) {
return false
}
return true
}
componentDidUpdate () { this.calculate() }
getLayoutData () {
const scrollOMatic = DOM.findDOMNode(this.scrollOMatic)
const scrollTray = DOM.findDOMNode(this.scrollTray)
const max = scrollOMatic.scrollHeight
const win = scrollOMatic.offsetHeight
const currentVal = this.state.animValues
const bounds = -(max - win)
const trayTop = scrollTray.offsetTop
const trayOffsetHeight = scrollTray.offsetHeight
const trayScrollHeight = scrollTray.scrollHeight
const scrollOMaticRect = scrollOMatic.getBoundingClientRect()
const scrollOMaticTop = scrollOMaticRect.top
const scrollOMaticHeight = scrollOMaticRect.height
const scrollOMaticOffsetHeight = scrollOMatic.offsetHeight
return {
currentVal,
bounds,
scrollTray,
trayTop,
trayOffsetHeight,
trayScrollHeight,
scrollOMatic,
scrollOMaticTop,
scrollOMaticHeight,
scrollOMaticOffsetHeight,
scrollOMaticRect
}
}
calculate () {
const layout = this.getLayoutData()
clearTimeout(this.calculate.timer)
this.calculate.timer = setTimeout(() => {
const max = layout.trayScrollHeight
const win = layout.scrollOMaticOffsetHeight
const currentVal = this.state.animValues
const bounds = -(max - win)
if (currentVal >= 1) {
this.resetMin()
} else if (currentVal <= bounds) {
const x = bounds + 1
this.resetMax(x)
}
})
}
resetMin () { this.setState({ animValues: 0 }) }
resetMax (x) { this.setState({ animValues: x }) }
canIscroll () {
const layout = this.getLayoutData()
return layout.trayOffsetTop < layout.scrollOMaticTop ||
layout.trayOffsetHeight > layout.scrollOMaticHeight
}
handleWheel (e) {
e.preventDefault()
const rawData = e.deltaY ? e.deltaY : e.deltaX
const mouseY = Math.floor(rawData)
const animationVal = this.state.animValues
const newAnimationVal = (animationVal + mouseY)
const newAnimationValNeg = (animationVal - mouseY)
if (!this.canIscroll()) return
const layout = this.getLayoutData()
const { currentVal, scrollOMaticHeight, trayScrollHeight } = layout
const isEndOfPage = -(currentVal - scrollOMaticHeight) + 1 === trayScrollHeight
this.navigator()
const scrolling = () => {
this.state.scrollInverted
? this.setState({ animValues: newAnimationValNeg })
: this.setState({ animValues: newAnimationVal })
}
raf(scrolling)
}
navigator () {
const layout = this.getLayoutData()
const { currentVal, scrollOMaticHeight, trayScrollHeight } = layout
const shouldBeNextRoute = -(currentVal - scrollOMaticHeight) + 1 >= trayScrollHeight
// const shouldBePrevRoute = this.state.animValues < 0
if (shouldBeNextRoute) {
Router.push(this.state.nextRoute)
}
// if (shouldBePrevRoute) {
// Router.push(this.state.prevRoute)
// }
}
render () {
const springConfig = presets.noWobble
return (
<div style={{ position: 'relative', width: '100vw', height: '100vh' }} className='scroll-o-matic' onWheel={this.handleWheel}
ref={scrollOMatic => { this.scrollOMatic = scrollOMatic }}>
<Motion style={{ z: spring(this.state.animValues, springConfig) }}>
{ ({ z }) => (
<div className='scroll-tray' ref={(scrollTray) => { this.scrollTray = scrollTray }}
style={{
height: '300vh',
width: '100vw',
// top: '-100vh',
transform: `translate3d(0,${z.toFixed(3)}px,0)`,
willChange: 'transform',
display: 'inline-flex',
position: 'absolute'
}}>
{ this.props.children }
</div>
)}
</Motion>
<style jsx>{`
.scroll-o-matic {
background-color: ${this.state.currentColor};
}
`}</style>
</div>
)
}
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
код сервера:
const express = require('express')
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const port = process.env.PORT || 3000
const handle = app.getRequestHandler()
app.prepare()
.then(() => {
const server = express()
server.use('/static', express.static('static'))
server.get('/', (req, res) => {
return app.render(req, res, '/', req.query)
})
server.get('/us', (req, res) => {
return app.render(req, res, '/us', req.query)
})
server.get('/work', (req, res) => {
return app.render(req, res, '/work', req.query)
})
server.get('/services', (req, res) => {
return app.render(req, res, '/services', req.query)
})
server.get('/converse', (req, res) => {
return app.render(req, res, '/converse', req.query)
})
server.get('*', (req, res) => {
return handle(req, res, '/', req.query)
})
server.listen(port, (err) => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
Может быть, я не понимаю «предварительную выборку» Next, но, похоже, она ничего не выполняет. Я в основном смущен, почему происходит загрузка страницы на стороне сервера. Даже при использовании обработчика запросов с подстановочными знаками в экспрессе страница перезагружается (но перенаправляет на себя?), Так что я не думаю, что это проблема с конфигурацией сервера.
Чего мне не хватает, оракулов интернета?