This repository has been archived by the owner on Jun 26, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
4,729 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"plugins": ["@babel/plugin-proposal-class-properties","@babel/plugin-proposal-function-bind"], | ||
"presets": [ | ||
["@babel/preset-env", { | ||
"modules": false | ||
}], | ||
"@babel/preset-react" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
# system files | ||
*.log | ||
.DS_Store* | ||
.idea/ | ||
|
||
# push to github | ||
!.babelrc | ||
!.gitignore | ||
!.npmignore | ||
|
||
# files | ||
node_modules/ | ||
dist/ | ||
src/style.js | ||
src/style.css |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// system file | ||
*.log | ||
.DS_Store* | ||
.idea/ | ||
|
||
// specific files | ||
node_modules/ | ||
example/ | ||
test/ | ||
bin/ | ||
CHANGELOG.md | ||
src/ | ||
|
||
// setting files | ||
.babelrc | ||
.travis.yml |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
NODE_BIN = node_modules/.bin | ||
SRC = src | ||
DIST = dist | ||
|
||
lint: | ||
@echo Linting... | ||
@$(NODE_BIN)/standard --verbose | $(NODE_BIN)/snazzy src/index.js | ||
|
||
lint-fix: | ||
@echo Linting... | ||
@$(NODE_BIN)/standard --fix --verbose | $(NODE_BIN)/snazzy src/index.js | ||
|
||
convertCSS: | ||
@echo Converting css... | ||
@node bin/transferSass.js | ||
|
||
build: lint-fix | ||
@echo Removing dist folder... | ||
@rm -rf dist && mkdir dist | ||
@echo Converting sass to style.css... | ||
@make convertCSS | ||
@echo Building js files... | ||
@$(NODE_BIN)/babel $(SRC) --out-dir $(DIST) | ||
@echo success! | ||
|
||
.PHONY: lint convertCSS lint-fix build |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
# react-horizontal-scrolling | ||
[![Version](http://img.shields.io/npm/v/react-horizontal-scrolling.svg)](https://www.npmjs.org/package/react-horizontal-scrolling) | ||
[![js-standard-style](https://img.shields.io/badge/code%20style-standard-brightgreen.svg?style=flat)](https://github.com/feross/standard) | ||
[![npm download][download-image]][download-url] | ||
|
||
[download-image]: https://img.shields.io/npm/dm/react-horizontal-scrolling.svg?style=flat-square | ||
[download-url]: https://npmjs.org/package/react-horizontal-scrolling | ||
|
||
## Installation | ||
|
||
```sh | ||
npm install react-horizontal-scrolling | ||
``` | ||
|
||
## Features | ||
* Support horizontal dragging | ||
* Support vertical mouse wheel | ||
* Tween animation (mainly copied from [malihu-custom-scrollbar-plugin](https://github.com/malihu/malihu-custom-scrollbar-plugin)) | ||
|
||
## Usage | ||
|
||
```js | ||
import HorizontalScroll from 'react-horizontal-scrolling' | ||
|
||
<HorizontalScroll> | ||
{pictures.map((pic, idx) => ( | ||
<img | ||
className="rounded w-56 h-64 object-cover" | ||
src={pic} | ||
key={idx} /> | ||
))} | ||
</HorizontalScroll> | ||
``` | ||
|
||
![](https://user-images.githubusercontent.com/5305874/103135320-6eceab00-46f2-11eb-80c7-9ff50842b078.gif) | ||
|
||
|
||
## License | ||
|
||
MIT |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
const sass = require('node-sass') | ||
const fs = require('fs') | ||
const path = require('path') | ||
|
||
function transferSass () { | ||
sass.render({ | ||
file: path.resolve(__dirname, '../src/index.scss'), | ||
outputStyle: 'compressed' | ||
}, function (err, result) { | ||
if (err) { | ||
console.log(err) | ||
return | ||
} | ||
const cssSource = result.css.toString() | ||
fs.writeFile(path.resolve(__dirname, '../src/style.js'), "export default '" + cssSource.replace(/\n/g, '') + "'", function (err) { | ||
if (err) { | ||
console.error(err) | ||
} | ||
console.log('css file has been transformed to JS successful') | ||
fs.writeFile(path.resolve(__dirname, '../src/style.css'), cssSource, function (err) { | ||
if (err) { | ||
console.error(err) | ||
} | ||
console.log('css file has been transformed successful') | ||
process.exit() | ||
}) | ||
}) | ||
}) | ||
} | ||
|
||
transferSass() | ||
|
||
fs.watch(path.resolve(__dirname, '../src/index.scss'), function (event, filename) { | ||
console.log(event, filename) | ||
transferSass() | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
{ | ||
"name": "react-horizontal-scrolling", | ||
"version": "0.1.0", | ||
"description": "react horizontal scroll component", | ||
"main": "dist/index.js", | ||
"scripts": {}, | ||
"keywords": [ | ||
"react", | ||
"horizontal", | ||
"scroll" | ||
], | ||
"standard": { | ||
"parser": "babel-eslint", | ||
"ignore": [ | ||
"dist/", | ||
"src/style.js", | ||
"src/style.css" | ||
] | ||
}, | ||
"peerDependencies": { | ||
"react": ">=^16.0.0", | ||
"react-dom": ">=^16.0.0" | ||
}, | ||
"devDependencies": { | ||
"@babel/cli": "^7.12.10", | ||
"@babel/core": "^7.12.10", | ||
"@babel/plugin-proposal-class-properties": "^7.12.1", | ||
"@babel/plugin-proposal-function-bind": "^7.12.1", | ||
"@babel/preset-env": "^7.12.11", | ||
"@babel/preset-react": "^7.12.10", | ||
"babel-eslint": "^10.1.0", | ||
"node-sass": "^5.0.0", | ||
"snazzy": "^9.0.0", | ||
"standard": "^16.0.3" | ||
}, | ||
"author": "chilllib", | ||
"license": "MIT", | ||
"files": [ | ||
"dist" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,201 @@ | ||
import React, { Component, createRef } from 'react' | ||
import PropTypes from 'prop-types' | ||
|
||
/* CSS */ | ||
import cssStyle from './style' | ||
|
||
export default class HorizontalScroll extends Component { | ||
static propTypes = { | ||
className: PropTypes.string | ||
} | ||
|
||
constructor (props) { | ||
super(props) | ||
|
||
this.state = { | ||
outerWidth: 0, | ||
innerWidth: 0, | ||
trackWidth: 0, | ||
trackLeftMax: 0 | ||
} | ||
|
||
this.outerId = 'horizontalScrollOuter' + Math.random().toFixed(5).replace('0.', '') | ||
this.innerId = 'horizontalScrollInner' + Math.random().toFixed(5).replace('0.', '') | ||
this.trackId = 'horizontalScrollTrack' + Math.random().toFixed(5).replace('0.', '') | ||
|
||
this.wrapperRef = createRef() | ||
this.onGlobalWheel = this.onGlobalWheel.bind(this) | ||
|
||
this.progress = 0 | ||
this.delay = 1000 / 60 | ||
} | ||
|
||
componentDidMount () { | ||
this.setStyleHeader() // Set the style to the <link> | ||
this.setupConfigBasedOnHTML() | ||
document.body.addEventListener('wheel', this.onGlobalWheel, { passive: false }) | ||
} | ||
|
||
componentWillUnmount () { | ||
document.body.removeEventListener('wheel', this.onGlobalWheel) | ||
} | ||
|
||
setupConfigBasedOnHTML () { | ||
setTimeout(() => { | ||
const outerElement = document.getElementById(this.outerId) | ||
const innerElement = document.getElementById(this.innerId) | ||
if (!outerElement || !innerElement) return this.setupConfigBasedOnHTML() | ||
|
||
const outerWidth = outerElement.offsetWidth | ||
const innerWidth = innerElement.offsetWidth | ||
const trackWidth = (outerWidth / innerWidth).toFixed(2) | ||
this.setState({ | ||
outerWidth, | ||
innerWidth, | ||
trackWidth: trackWidth, | ||
trackLeftMax: (1 - parseFloat(trackWidth)) * outerWidth | ||
}) | ||
}, 500) | ||
} | ||
|
||
onGlobalWheel (e) { | ||
if (this.wrapperRef && this.wrapperRef.current.contains(e.target)) { | ||
e.preventDefault() | ||
} | ||
} | ||
|
||
updateTrackLeft (trackLeft) { | ||
const { trackLeftMax, outerWidth, innerWidth } = this.state | ||
trackLeft = Math.max(trackLeft, 0) | ||
trackLeft = Math.min(trackLeft, trackLeftMax) | ||
document.getElementById(this.trackId).style.left = trackLeft + 'px' | ||
|
||
const innerTransformFrom = parseFloat(document.getElementById(this.innerId).style.transform.replace(/[^\d.]/g, '')) || 0 | ||
const innerTransformTo = ((trackLeft / outerWidth) * innerWidth).toFixed(2) | ||
this.updateInnerTransform(innerTransformFrom, innerTransformTo) | ||
} | ||
|
||
updateInnerTransform (from, to) { | ||
if (from === to) return | ||
const diff = to - from | ||
const duration = 950 | ||
const startTime = new Date() | ||
const delay = 1000 / 60 | ||
let progress = 0 | ||
let time = delay | ||
|
||
if (this.animationFrameId) { | ||
window.cancelAnimationFrame(this.animationFrameId) | ||
this.animationFrameId = null | ||
} | ||
|
||
const step = () => { | ||
progress = new Date() - startTime | ||
const tweenValue = this.tween({ from, time, duration, diff }) | ||
document.getElementById(this.innerId).style.transform = `translateX(-${tweenValue}px)` | ||
|
||
if (progress >= time) { | ||
time = progress > time | ||
? progress + delay - (progress - time) | ||
: progress + delay - 1 | ||
if (time < progress + 1) { | ||
time = progress + 1 | ||
} | ||
} | ||
|
||
if (time < duration) { | ||
this.animationFrameId = window.requestAnimationFrame(step) | ||
} | ||
} | ||
|
||
this.animationFrameId = window.requestAnimationFrame(step) | ||
} | ||
|
||
tween ({ from, time, duration, diff }) { | ||
const myTime = time / duration | ||
const ts = myTime * myTime | ||
const tc = ts * myTime | ||
return from + diff * (0.499999999999997 * tc * ts + -2.5 * ts * ts + 5.5 * tc + -6.5 * ts + 4 * myTime) | ||
} | ||
|
||
onWheel (e) { | ||
const currentTrackLeft = document.getElementById(this.trackId).style.left || '0px' | ||
this.updateTrackLeft(parseFloat(currentTrackLeft.replace('px', '')) + e.deltaY) | ||
} | ||
|
||
onTrackMouseDown (e) { | ||
e.stopPropagation() | ||
e.preventDefault() | ||
this.prePosX = null | ||
document.onmousemove = this.onTrackMouseMove.bind(this) | ||
document.onmouseup = this.onTrackMouseUp.bind(this) | ||
} | ||
|
||
onTrackMouseMove (e) { | ||
e.preventDefault() | ||
const element = document.getElementById(this.trackId) | ||
const pos1 = !this.prePosX ? 0 : this.prePosX - e.clientX | ||
this.prePosX = e.clientX | ||
this.updateTrackLeft(element.offsetLeft - pos1) | ||
} | ||
|
||
async onTrackMouseUp () { | ||
document.onmouseup = null | ||
document.onmousemove = null | ||
} | ||
|
||
/** | ||
* Set style tag in header | ||
* in this way we can insert default css | ||
*/ | ||
setStyleHeader () { | ||
const head = document.getElementsByTagName('head')[0] | ||
if (!head.querySelector('style[id="react-horizontal-scrolling"]')) { | ||
const tag = document.createElement('style') | ||
tag.id = 'react-horizontal-scrolling' | ||
tag.innerHTML = cssStyle | ||
/* eslint-disable */ | ||
if (typeof __webpack_nonce__ !== 'undefined' && __webpack_nonce__) { | ||
tag.setAttribute('nonce', __webpack_nonce__) | ||
} | ||
/* eslint-enable */ | ||
head.insertBefore(tag, head.firstChild) | ||
} | ||
} | ||
|
||
render () { | ||
const { className, children } = this.props | ||
const { trackWidth } = this.state | ||
const isArrayChild = Array.isArray(children) | ||
return ( | ||
<div className={`HorizontalScroll ${className || ''}`} ref={this.wrapperRef}> | ||
<div className='HorizontalScrollOuter' id={this.outerId} onWheel={::this.onWheel}> | ||
<div | ||
id={this.innerId} | ||
className='HorizontalScrollInner' | ||
> | ||
{!isArrayChild && | ||
<div className='HorizontalScrollInnerChildren'>{children}</div>} | ||
{isArrayChild && | ||
children.map((child, idx) => ( | ||
<div key={idx} className='HorizontalScrollInnerChildren'> | ||
{child} | ||
</div> | ||
))} | ||
</div> | ||
</div> | ||
{trackWidth > 0 && trackWidth < 1 && | ||
<div className='HorizontalScrollTrack'> | ||
<div | ||
id={this.trackId} | ||
className='HorizontalScrollTrackInner' | ||
style={{ | ||
width: parseFloat(trackWidth) * 100 + '%' | ||
}} | ||
onMouseDown={::this.onTrackMouseDown} | ||
/> | ||
</div>} | ||
</div> | ||
) | ||
} | ||
} |
Oops, something went wrong.