Merge pull request #261 from paulkaplan/touch-sliders

Make color pick touch accessible and jump on click
This commit is contained in:
Paul Kaplan 2018-01-22 09:17:29 -05:00 committed by GitHub
commit efe30ec50d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 53 additions and 17 deletions

View file

@ -1,6 +1,7 @@
import React from 'react'; import React from 'react';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import bindAll from 'lodash.bindall'; import bindAll from 'lodash.bindall';
import {getEventXY} from '../../lib/touch-utils';
import styles from './slider.css'; import styles from './slider.css';
@ -14,25 +15,39 @@ class SliderComponent extends React.Component {
'handleMouseDown', 'handleMouseDown',
'handleMouseUp', 'handleMouseUp',
'handleMouseMove', 'handleMouseMove',
'handleClickBackground',
'setBackground' 'setBackground'
]); ]);
} }
handleMouseDown () { handleMouseDown () {
document.addEventListener('mouseup', this.handleMouseUp);
document.addEventListener('mousemove', this.handleMouseMove); document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('mouseup', this.handleMouseUp);
document.addEventListener('touchmove', this.handleMouseMove);
document.addEventListener('touchend', this.handleMouseUp);
} }
handleMouseUp () { handleMouseUp () {
document.removeEventListener('mouseup', this.handleMouseUp);
document.removeEventListener('mousemove', this.handleMouseMove); document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
document.removeEventListener('touchmove', this.handleMouseMove);
document.removeEventListener('touchend', this.handleMouseUp);
} }
handleMouseMove (event) { handleMouseMove (event) {
event.preventDefault(); event.preventDefault();
this.props.onChange(this.scaleMouseToSliderPosition(event));
}
handleClickBackground (event) {
this.props.onChange(this.scaleMouseToSliderPosition(event));
}
scaleMouseToSliderPosition (event){
const {x} = getEventXY(event);
const backgroundBBox = this.background.getBoundingClientRect(); const backgroundBBox = this.background.getBoundingClientRect();
const x = event.clientX - backgroundBBox.left; const scaledX = x - backgroundBBox.left;
this.props.onChange(Math.max(0, Math.min(100, 100 * x / backgroundBBox.width))); return Math.max(0, Math.min(100, 100 * scaledX / backgroundBBox.width));
} }
setBackground (ref) { setBackground (ref) {
@ -53,6 +68,7 @@ class SliderComponent extends React.Component {
style={{ style={{
backgroundImage: this.props.background backgroundImage: this.props.background
}} }}
onClick={this.handleClickBackground}
> >
<div <div
className={styles.handle} className={styles.handle}
@ -60,6 +76,7 @@ class SliderComponent extends React.Component {
left: `${handleOffset}px` left: `${handleOffset}px`
}} }}
onMouseDown={this.handleMouseDown} onMouseDown={this.handleMouseDown}
onTouchStart={this.handleMouseDown}
/> />
</div> </div>
); );

View file

@ -28,9 +28,9 @@ const hsvToHex = (h, s, v) =>
parseColor(`hsv(${3.6 * h}, ${s}, ${v})`).hex parseColor(`hsv(${3.6 * h}, ${s}, ${v})`).hex
; ;
// Important! This component ignores new color props and cannot be updated // Important! This component ignores new color props except when isEyeDropping
// This is to make the HSV <=> RGB conversion stable. Because of this, the // This is to make the HSV <=> RGB conversion stable. The sliders manage their
// component MUST be unmounted in order to change the props externally. // own changes until unmounted or color changes with props.isEyeDropping = true.
class ColorPicker extends React.Component { class ColorPicker extends React.Component {
constructor (props) { constructor (props) {
super(props); super(props);
@ -51,7 +51,7 @@ class ColorPicker extends React.Component {
}; };
} }
componentWillReceiveProps (newProps) { componentWillReceiveProps (newProps) {
if (this.props.color !== newProps.color) { if (this.props.isEyeDropping && this.props.color !== newProps.color) {
// color set by eye dropper, so update slider states // color set by eye dropper, so update slider states
const hsv = this.getHsv(newProps.color); const hsv = this.getHsv(newProps.color);
this.setState({ this.setState({
@ -68,16 +68,19 @@ class ColorPicker extends React.Component {
[50, 100, 100] : colorStringToHsv(color); [50, 100, 100] : colorStringToHsv(color);
} }
handleHueChange (hue) { handleHueChange (hue) {
this.setState({hue: hue}); this.setState({hue: hue}, () => {
this.handleColorChange(); this.handleColorChange();
});
} }
handleSaturationChange (saturation) { handleSaturationChange (saturation) {
this.setState({saturation: saturation}); this.setState({saturation: saturation}, () => {
this.handleColorChange(); this.handleColorChange();
});
} }
handleBrightnessChange (brightness) { handleBrightnessChange (brightness) {
this.setState({brightness: brightness}); this.setState({brightness: brightness}, () => {
this.handleColorChange(); this.handleColorChange();
});
} }
handleColorChange () { handleColorChange () {
this.props.onChangeColor(hsvToHex( this.props.onChangeColor(hsvToHex(

View file

@ -140,14 +140,14 @@ class PaintEditor extends React.Component {
const callback = this.props.changeColorToEyeDropper; const callback = this.props.changeColorToEyeDropper;
this.eyeDropper.remove(); this.eyeDropper.remove();
this.props.previousTool.activate();
this.props.onDeactivateEyeDropper();
this.stopEyeDroppingLoop();
if (!this.eyeDropper.hideLoupe) { if (!this.eyeDropper.hideLoupe) {
// If not hide loupe, that means the click is inside the canvas, // If not hide loupe, that means the click is inside the canvas,
// so apply the new color // so apply the new color
callback(colorString); callback(colorString);
} }
this.props.previousTool.activate();
this.props.onDeactivateEyeDropper();
this.stopEyeDroppingLoop();
this.setState({colorInfo: null}); this.setState({colorInfo: null});
} }
} }

16
src/lib/touch-utils.js Normal file
View file

@ -0,0 +1,16 @@
/* DO NOT EDIT
@todo This file is copied from GUI and should be pulled out into a shared library.
See https://github.com/LLK/scratch-paint/issues/13 */
const getEventXY = e => {
if (e.touches && e.touches[0]) {
return {x: e.touches[0].clientX, y: e.touches[0].clientY};
} else if (e.changedTouches && e.changedTouches[0]) {
return {x: e.changedTouches[0].clientX, y: e.changedTouches[0].clientY};
}
return {x: e.clientX, y: e.clientY};
};
export {
getEventXY
};