Skip to content
This repository has been archived by the owner on Jun 26, 2024. It is now read-only.

Commit

Permalink
init
Browse files Browse the repository at this point in the history
  • Loading branch information
wwayne committed Dec 25, 2020
1 parent 72f81b8 commit b554dfe
Show file tree
Hide file tree
Showing 10 changed files with 4,729 additions and 0 deletions.
9 changes: 9 additions & 0 deletions .babelrc
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"
]
}
15 changes: 15 additions & 0 deletions .gitignore
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
16 changes: 16 additions & 0 deletions .npmignore
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
26 changes: 26 additions & 0 deletions Makefile
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
40 changes: 40 additions & 0 deletions README.md
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
36 changes: 36 additions & 0 deletions bin/transferSass.js
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()
})
41 changes: 41 additions & 0 deletions package.json
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"
]
}
201 changes: 201 additions & 0 deletions src/index.js
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>
)
}
}
Loading

0 comments on commit b554dfe

Please sign in to comment.