mirror of
https://github.com/infeng/react-viewer.git
synced 2025-12-08 17:36:40 +00:00
refactor: hooks
This commit is contained in:
parent
266d59bf2b
commit
09ecbc2be6
@ -20,8 +20,8 @@ interface State {
|
||||
class App extends React.Component<any, Partial<State>> {
|
||||
container: HTMLDivElement;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
visible: false,
|
||||
@ -121,12 +121,14 @@ class App extends React.Component<any, Partial<State>> {
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<div className={inlineContainerClass} ref={ref => {this.container = ref;}}></div>
|
||||
<div className={inlineContainerClass} ref={ref => {this.container = ref; }}></div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Viewer
|
||||
visible={this.state.visible}
|
||||
onClose={() => { this.setState({ visible: false }); } }
|
||||
onClose={() => {
|
||||
this.setState({ visible: false });
|
||||
}}
|
||||
images={images}
|
||||
activeIndex={this.state.activeIndex}
|
||||
container={inline ? this.container : null}
|
||||
@ -154,5 +156,5 @@ class App extends React.Component<any, Partial<State>> {
|
||||
|
||||
ReactDOM.render(
|
||||
<App />,
|
||||
document.getElementById('root')
|
||||
document.getElementById('root'),
|
||||
);
|
||||
|
||||
59
package-lock.json
generated
59
package-lock.json
generated
@ -123,16 +123,26 @@
|
||||
"integrity": "sha512-vToa8YEeulfyYg1gSOeHjvvIRqrokng62VMSj2hoZrwZNcYrp2h3AWo6KeBVuymIklQUaY5zgVJvVsC4KiiLkQ==",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "0.14.57",
|
||||
"resolved": "https://registry.npmjs.org/@types/react/-/react-0.14.57.tgz",
|
||||
"integrity": "sha1-GHioZU+v3R04G4RXKStkM0mMW2I=",
|
||||
"@types/prop-types": {
|
||||
"version": "15.7.1",
|
||||
"resolved": "https://registry.npm.taobao.org/@types/prop-types/download/@types/prop-types-15.7.1.tgz",
|
||||
"integrity": "sha1-8aEee6uww8rWgQC+OB0eBkxo8fY=",
|
||||
"dev": true
|
||||
},
|
||||
"@types/react": {
|
||||
"version": "16.9.1",
|
||||
"resolved": "https://registry.npm.taobao.org/@types/react/download/@types/react-16.9.1.tgz?cache=0&sync_timestamp=1565360509473&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2F%40types%2Freact%2Fdownload%2F%40types%2Freact-16.9.1.tgz",
|
||||
"integrity": "sha1-hiyDtMnVzRFuQv2aTzaUhDzSwFE=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/prop-types": "*",
|
||||
"csstype": "^2.2.0"
|
||||
}
|
||||
},
|
||||
"@types/react-dom": {
|
||||
"version": "0.14.23",
|
||||
"resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-0.14.23.tgz",
|
||||
"integrity": "sha1-zs/PrXVLTCdl/l0puBswGImtbC4=",
|
||||
"version": "16.8.5",
|
||||
"resolved": "https://registry.npm.taobao.org/@types/react-dom/download/@types/react-dom-16.8.5.tgz",
|
||||
"integrity": "sha1-Pj9NmRmTkaf7QKo6FVyN2ZuJnL0=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@types/react": "*"
|
||||
@ -2840,6 +2850,12 @@
|
||||
"cssom": "0.3.x"
|
||||
}
|
||||
},
|
||||
"csstype": {
|
||||
"version": "2.6.6",
|
||||
"resolved": "https://registry.npm.taobao.org/csstype/download/csstype-2.6.6.tgz",
|
||||
"integrity": "sha1-w0+CJqlLuxDDLMDXFK/flCKR/EE=",
|
||||
"dev": true
|
||||
},
|
||||
"dashdash": {
|
||||
"version": "1.14.1",
|
||||
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||
@ -11119,27 +11135,26 @@
|
||||
}
|
||||
},
|
||||
"react": {
|
||||
"version": "16.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react/-/react-16.4.2.tgz",
|
||||
"integrity": "sha512-dMv7YrbxO4y2aqnvA7f/ik9ibeLSHQJTI6TrYAenPSaQ6OXfb+Oti+oJiy8WBxgRzlKatYqtCjphTgDSCEiWFg==",
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npm.taobao.org/react/download/react-16.9.0.tgz?cache=0&sync_timestamp=1565317868688&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Freact%2Fdownload%2Freact-16.9.0.tgz",
|
||||
"integrity": "sha1-QLovmvE7waONddvy9DWaUYXE96o=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fbjs": "^0.8.16",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.0"
|
||||
"prop-types": "^15.6.2"
|
||||
}
|
||||
},
|
||||
"react-dom": {
|
||||
"version": "16.4.2",
|
||||
"resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.4.2.tgz",
|
||||
"integrity": "sha512-Usl73nQqzvmJN+89r97zmeUpQDKDlh58eX6Hbs/ERdDHzeBzWy+ENk7fsGQ+5KxArV1iOFPT46/VneklK9zoWw==",
|
||||
"version": "16.9.0",
|
||||
"resolved": "https://registry.npm.taobao.org/react-dom/download/react-dom-16.9.0.tgz",
|
||||
"integrity": "sha1-XmVSel4m8irjcBExvMyu6fsNOWI=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"fbjs": "^0.8.16",
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"prop-types": "^15.6.0"
|
||||
"prop-types": "^15.6.2",
|
||||
"scheduler": "^0.15.0"
|
||||
}
|
||||
},
|
||||
"react-is": {
|
||||
@ -12143,6 +12158,16 @@
|
||||
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==",
|
||||
"dev": true
|
||||
},
|
||||
"scheduler": {
|
||||
"version": "0.15.0",
|
||||
"resolved": "https://registry.npm.taobao.org/scheduler/download/scheduler-0.15.0.tgz?cache=0&sync_timestamp=1565317903688&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Fscheduler%2Fdownload%2Fscheduler-0.15.0.tgz",
|
||||
"integrity": "sha1-a/z4D/hQsoD+1K7sxlE7wLTxf44=",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"loose-envify": "^1.1.0",
|
||||
"object-assign": "^4.1.1"
|
||||
}
|
||||
},
|
||||
"semver": {
|
||||
"version": "5.5.0",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
|
||||
|
||||
@ -34,8 +34,8 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "^23.3.1",
|
||||
"@types/node": "^6.0.45",
|
||||
"@types/react": "^0.14.39",
|
||||
"@types/react-dom": "^0.14.17",
|
||||
"@types/react": "^16.9.1",
|
||||
"@types/react-dom": "^16.8.5",
|
||||
"antd": "^3.16.2",
|
||||
"atool-build": "^1.0.8",
|
||||
"babel-jest": "^23.4.2",
|
||||
@ -61,8 +61,8 @@
|
||||
"jest-static-stubs": "0.0.1",
|
||||
"merge2": "^1.0.2",
|
||||
"pre-commit": "^1.1.3",
|
||||
"react": "^16.2.0",
|
||||
"react-dom": "^16.2.0",
|
||||
"react": "^16.9.0",
|
||||
"react-dom": "^16.9.0",
|
||||
"react-test-render": "^1.1.1",
|
||||
"through2": "^2.0.1",
|
||||
"ts-jest": "^23.1.3",
|
||||
|
||||
@ -4,18 +4,12 @@ export interface LoadingProps {
|
||||
style?: React.CSSProperties;
|
||||
}
|
||||
|
||||
export default class Loading extends React.Component<LoadingProps, any> {
|
||||
constructor() {
|
||||
super();
|
||||
}
|
||||
|
||||
render() {
|
||||
let cls = 'circle-loading';
|
||||
return (
|
||||
<div className="loading-wrap" style={this.props.style}>
|
||||
<div className={cls}>
|
||||
</div>
|
||||
export default function Loading(props: LoadingProps) {
|
||||
let cls = 'circle-loading';
|
||||
return (
|
||||
<div className="loading-wrap" style={props.style}>
|
||||
<div className={cls}>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
96
src/Viewer copy.tsx
Normal file
96
src/Viewer copy.tsx
Normal file
@ -0,0 +1,96 @@
|
||||
import * as React from 'react';
|
||||
import * as ReactDOM from 'react-dom';
|
||||
import ViewerCore from './ViewerCore';
|
||||
import ViewerProps from './ViewerProps';
|
||||
|
||||
export default class Viewer extends React.Component<ViewerProps, any> {
|
||||
private defaultContainer: HTMLElement;
|
||||
private container: HTMLElement;
|
||||
private component: React.ReactNode;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.container = null;
|
||||
this.defaultContainer = null;
|
||||
if (typeof document !== 'undefined') {
|
||||
this.setDefaultContainer();
|
||||
}
|
||||
this.component = null;
|
||||
}
|
||||
|
||||
setDefaultContainer() {
|
||||
this.defaultContainer = document.createElement('div');
|
||||
}
|
||||
|
||||
renderViewer() {
|
||||
if (this.props.visible || this.component) {
|
||||
if (!this.container) {
|
||||
if (this.props.container) {
|
||||
this.container = this.props.container;
|
||||
} else {
|
||||
if (!this.defaultContainer) {
|
||||
this.setDefaultContainer();
|
||||
}
|
||||
this.container = this.defaultContainer;
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
}
|
||||
let instance = this;
|
||||
ReactDOM.unstable_renderSubtreeIntoContainer(
|
||||
this,
|
||||
<ViewerCore
|
||||
{...this.props}
|
||||
/>,
|
||||
this.container,
|
||||
function () {
|
||||
instance.component = this;
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
removeViewer() {
|
||||
if (this.container) {
|
||||
const container = this.container;
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
container.parentNode.removeChild(container);
|
||||
this.container = null;
|
||||
this.component = null;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.props.visible && this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
this.removeViewer();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ViewerProps) {
|
||||
if (this.props.container !== nextProps.container) {
|
||||
this.component = null;
|
||||
if (nextProps.container) {
|
||||
if (this.container) {
|
||||
document.body.removeChild(this.container);
|
||||
}
|
||||
this.container = nextProps.container;
|
||||
} else {
|
||||
this.container = this.defaultContainer;
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.renderViewer();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.renderViewer();
|
||||
}
|
||||
|
||||
render() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
107
src/Viewer.tsx
107
src/Viewer.tsx
@ -3,94 +3,35 @@ import * as ReactDOM from 'react-dom';
|
||||
import ViewerCore from './ViewerCore';
|
||||
import ViewerProps from './ViewerProps';
|
||||
|
||||
export default class Viewer extends React.Component<ViewerProps, any> {
|
||||
private defaultContainer: HTMLElement;
|
||||
private container: HTMLElement;
|
||||
private component: React.ReactNode;
|
||||
export default React.forwardRef((props: ViewerProps, ref) => {
|
||||
const defaultContainer = React.useRef(document.createElement('div'));
|
||||
const [ container, setContainer ] = React.useState(props.container);
|
||||
const [ init, setInit ] = React.useState(false);
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
React.useEffect(() => {
|
||||
document.body.appendChild(defaultContainer.current);
|
||||
}, []);
|
||||
|
||||
this.container = null;
|
||||
this.defaultContainer = null;
|
||||
if (typeof document !== 'undefined') {
|
||||
this.setDefaultContainer();
|
||||
React.useEffect(() => {
|
||||
if (props.visible && !init) {
|
||||
setInit(true);
|
||||
}
|
||||
this.component = null;
|
||||
}
|
||||
}, [props.visible, init]);
|
||||
|
||||
setDefaultContainer() {
|
||||
this.defaultContainer = document.createElement('div');
|
||||
}
|
||||
|
||||
renderViewer() {
|
||||
if (this.props.visible || this.component) {
|
||||
if (!this.container) {
|
||||
if (this.props.container) {
|
||||
this.container = this.props.container;
|
||||
} else {
|
||||
if (!this.defaultContainer) {
|
||||
this.setDefaultContainer();
|
||||
}
|
||||
this.container = this.defaultContainer;
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
}
|
||||
let instance = this;
|
||||
ReactDOM.unstable_renderSubtreeIntoContainer(
|
||||
this,
|
||||
<ViewerCore
|
||||
{...this.props}
|
||||
/>,
|
||||
this.container,
|
||||
function () {
|
||||
instance.component = this;
|
||||
},
|
||||
);
|
||||
React.useEffect(() => {
|
||||
if (props.container) {
|
||||
setContainer(props.container);
|
||||
} else {
|
||||
setContainer(defaultContainer.current);
|
||||
}
|
||||
}
|
||||
}, [props.container]);
|
||||
|
||||
removeViewer() {
|
||||
if (this.container) {
|
||||
const container = this.container;
|
||||
ReactDOM.unmountComponentAtNode(container);
|
||||
container.parentNode.removeChild(container);
|
||||
this.container = null;
|
||||
this.component = null;
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.props.visible && this.props.onClose) {
|
||||
this.props.onClose();
|
||||
}
|
||||
this.removeViewer();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ViewerProps) {
|
||||
if (this.props.container !== nextProps.container) {
|
||||
this.component = null;
|
||||
if (nextProps.container) {
|
||||
if (this.container) {
|
||||
document.body.removeChild(this.container);
|
||||
}
|
||||
this.container = nextProps.container;
|
||||
} else {
|
||||
this.container = this.defaultContainer;
|
||||
document.body.appendChild(this.container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.renderViewer();
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.renderViewer();
|
||||
}
|
||||
|
||||
render() {
|
||||
if (!init) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return ReactDOM.createPortal((
|
||||
<ViewerCore
|
||||
{...props}
|
||||
/>
|
||||
), container);
|
||||
});
|
||||
|
||||
@ -28,154 +28,154 @@ export interface ViewerCanvasState {
|
||||
mouseY?: number;
|
||||
}
|
||||
|
||||
export default class ViewerCanvas extends React.Component<ViewerCanvasProps, ViewerCanvasState> {
|
||||
export default function ViewerCanvas(props: ViewerCanvasProps) {
|
||||
const isMouseDown = React.useRef(false);
|
||||
const prePosition = React.useRef({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
const [ position, setPosition ] = React.useState({
|
||||
x: 0,
|
||||
y: 0,
|
||||
});
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
this.state = {
|
||||
isMouseDown: false,
|
||||
mouseX: 0,
|
||||
mouseY: 0,
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
bindEvent(true);
|
||||
bindWindowResizeEvent(true);
|
||||
};
|
||||
}
|
||||
}, []);
|
||||
|
||||
componentDidMount() {
|
||||
if (this.props.drag) {
|
||||
this.bindEvent();
|
||||
React.useEffect(() => {
|
||||
bindWindowResizeEvent(!props.visible);
|
||||
}, [props.visible]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props.visible && props.drag) {
|
||||
bindEvent();
|
||||
}
|
||||
if (!props.visible && props.drag) {
|
||||
handleMouseUp({});
|
||||
}
|
||||
return () => {
|
||||
bindEvent(true);
|
||||
};
|
||||
}, [props.drag, props.visible]);
|
||||
|
||||
React.useEffect(() => {
|
||||
let diffX = position.x - prePosition.current.x;
|
||||
let diffY = position.y - prePosition.current.y;
|
||||
prePosition.current = {
|
||||
x: position.x,
|
||||
y: position.y,
|
||||
};
|
||||
props.onChangeImgState(props.width, props.height, props.top + diffY, props.left + diffX);
|
||||
}, [position]);
|
||||
|
||||
function handleResize(e) {
|
||||
props.onResize();
|
||||
}
|
||||
|
||||
handleResize = (e) => {
|
||||
this.props.onResize();
|
||||
function handleCanvasMouseDown(e) {
|
||||
props.onCanvasMouseDown(e);
|
||||
handleMouseDown(e);
|
||||
}
|
||||
|
||||
handleCanvasMouseDown = (e) => {
|
||||
this.props.onCanvasMouseDown(e);
|
||||
this.handleMouseDown(e);
|
||||
}
|
||||
|
||||
handleMouseDown = (e) => {
|
||||
function handleMouseDown(e) {
|
||||
if (e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (!this.props.visible || !this.props.drag) {
|
||||
if (!props.visible || !props.drag) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
this.setState({
|
||||
isMouseDown: true,
|
||||
mouseX: e.nativeEvent.clientX,
|
||||
mouseY: e.nativeEvent.clientY,
|
||||
});
|
||||
isMouseDown.current = true;
|
||||
prePosition.current = {
|
||||
x: e.nativeEvent.clientX,
|
||||
y: e.nativeEvent.clientY,
|
||||
};
|
||||
}
|
||||
|
||||
handleMouseMove = (e) => {
|
||||
if (this.state.isMouseDown) {
|
||||
let diffX = e.clientX - this.state.mouseX;
|
||||
let diffY = e.clientY - this.state.mouseY;
|
||||
this.setState({
|
||||
mouseX: e.clientX,
|
||||
mouseY: e.clientY,
|
||||
const handleMouseMove = (e) => {
|
||||
if (isMouseDown.current) {
|
||||
setPosition({
|
||||
x: e.clientX,
|
||||
y: e.clientY,
|
||||
});
|
||||
this.props.onChangeImgState(this.props.width, this.props.height, this.props.top + diffY, this.props.left + diffX);
|
||||
}
|
||||
};
|
||||
|
||||
function handleMouseUp(e) {
|
||||
isMouseDown.current = false;
|
||||
}
|
||||
|
||||
handleMouseUp = (e) => {
|
||||
this.setState({
|
||||
isMouseDown: false,
|
||||
});
|
||||
function bindWindowResizeEvent(remove?: boolean) {
|
||||
let funcName = 'addEventListener';
|
||||
if (remove) {
|
||||
funcName = 'removeEventListener';
|
||||
}
|
||||
window[funcName]('resize', handleResize, false);
|
||||
}
|
||||
|
||||
bindEvent = (remove?: boolean) => {
|
||||
function bindEvent(remove?: boolean) {
|
||||
let funcName = 'addEventListener';
|
||||
if (remove) {
|
||||
funcName = 'removeEventListener';
|
||||
}
|
||||
|
||||
document[funcName]('click', this.handleMouseUp, false);
|
||||
document[funcName]('mousemove', this.handleMouseMove, false);
|
||||
window[funcName]('resize', this.handleResize, false);
|
||||
document[funcName]('click', handleMouseUp, false);
|
||||
document[funcName]('mousemove', handleMouseMove, false);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ViewerCanvasProps) {
|
||||
if (!this.props.visible && nextProps.visible) {
|
||||
if (nextProps.drag) {
|
||||
return this.bindEvent();
|
||||
}
|
||||
}
|
||||
if (this.props.visible && !nextProps.visible) {
|
||||
this.handleMouseUp({});
|
||||
if (nextProps.drag) {
|
||||
return this.bindEvent(true);
|
||||
}
|
||||
}
|
||||
if (this.props.drag && !nextProps.drag) {
|
||||
return this.bindEvent(true);
|
||||
}
|
||||
if (!this.props.drag && nextProps.drag) {
|
||||
if (nextProps.visible) {
|
||||
return this.bindEvent(true);
|
||||
}
|
||||
}
|
||||
let imgStyle: React.CSSProperties = {
|
||||
width: `${props.width}px`,
|
||||
height: `${props.height}px`,
|
||||
transform: `
|
||||
translateX(${props.left !== null ? props.left + 'px' : 'aoto'}) translateY(${props.top}px)
|
||||
rotate(${props.rotate}deg) scaleX(${props.scaleX}) scaleY(${props.scaleY})`,
|
||||
};
|
||||
|
||||
const imgClass = classnames(`${props.prefixCls}-image`, {
|
||||
drag: props.drag,
|
||||
[`${props.prefixCls}-image-transition`]: !isMouseDown.current,
|
||||
});
|
||||
|
||||
let style = {
|
||||
zIndex: props.zIndex,
|
||||
};
|
||||
|
||||
let imgNode = null;
|
||||
if (props.imgSrc !== '') {
|
||||
imgNode = <img
|
||||
className={imgClass}
|
||||
src={props.imgSrc}
|
||||
style={imgStyle}
|
||||
onMouseDown={handleMouseDown}
|
||||
/>;
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.bindEvent(true);
|
||||
}
|
||||
|
||||
render() {
|
||||
let imgStyle: React.CSSProperties = {
|
||||
width: `${this.props.width}px`,
|
||||
height: `${this.props.height}px`,
|
||||
transform: `
|
||||
translateX(${this.props.left !== null ? this.props.left + 'px' : 'aoto'}) translateY(${this.props.top}px)
|
||||
rotate(${this.props.rotate}deg) scaleX(${this.props.scaleX}) scaleY(${this.props.scaleY})`,
|
||||
};
|
||||
|
||||
const imgClass = classnames(`${this.props.prefixCls}-image`, {
|
||||
drag: this.props.drag,
|
||||
[`${this.props.prefixCls}-image-transition`]: !this.state.isMouseDown,
|
||||
});
|
||||
|
||||
let style = {
|
||||
zIndex: this.props.zIndex,
|
||||
};
|
||||
|
||||
let imgNode = null;
|
||||
if (this.props.imgSrc !== '') {
|
||||
imgNode = <img
|
||||
className={imgClass}
|
||||
src={this.props.imgSrc}
|
||||
style={imgStyle}
|
||||
onMouseDown={this.handleMouseDown}
|
||||
/>;
|
||||
}
|
||||
if (this.props.loading) {
|
||||
imgNode = (
|
||||
<div
|
||||
style={{
|
||||
display: 'flex',
|
||||
height: `${window.innerHeight - 84}px`,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Loading/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
if (props.loading) {
|
||||
imgNode = (
|
||||
<div
|
||||
className={`${this.props.prefixCls}-canvas`}
|
||||
onMouseDown={this.handleCanvasMouseDown}
|
||||
style={style}
|
||||
style={{
|
||||
display: 'flex',
|
||||
height: `${window.innerHeight - 84}px`,
|
||||
justifyContent: 'center',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{imgNode}
|
||||
<Loading/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`${props.prefixCls}-canvas`}
|
||||
onMouseDown={handleCanvasMouseDown}
|
||||
style={style}
|
||||
>
|
||||
{imgNode}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
679
src/ViewerCore copy.tsx
Normal file
679
src/ViewerCore copy.tsx
Normal file
@ -0,0 +1,679 @@
|
||||
import * as React from 'react';
|
||||
import './style/index.less';
|
||||
import ViewerCanvas from './ViewerCanvas';
|
||||
import ViewerNav from './ViewerNav';
|
||||
import ViewerToolbar, { defaultToolbars } from './ViewerToolbar';
|
||||
import ViewerProps, { ImageDecorator, ToolbarConfig } from './ViewerProps';
|
||||
import Icon, { ActionType } from './Icon';
|
||||
import * as constants from './constants';
|
||||
|
||||
function noop() { }
|
||||
|
||||
const transitionDuration = 300;
|
||||
|
||||
export interface ViewerCoreState {
|
||||
visible?: boolean;
|
||||
visibleStart?: boolean;
|
||||
transitionEnd?: boolean;
|
||||
activeIndex?: number;
|
||||
width?: number;
|
||||
height?: number;
|
||||
top?: number;
|
||||
left?: number;
|
||||
rotate?: number;
|
||||
imageWidth?: number;
|
||||
imageHeight?: number;
|
||||
scaleX?: number;
|
||||
scaleY?: number;
|
||||
loading?: boolean;
|
||||
loadFailed?: boolean;
|
||||
}
|
||||
|
||||
export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreState> {
|
||||
static defaultProps: Partial<ViewerProps> = {
|
||||
visible: false,
|
||||
onClose: noop,
|
||||
images: [],
|
||||
activeIndex: 0,
|
||||
zIndex: 1000,
|
||||
drag: true,
|
||||
attribute: true,
|
||||
zoomable: true,
|
||||
rotatable: true,
|
||||
scalable: true,
|
||||
onMaskClick: noop,
|
||||
changeable: true,
|
||||
customToolbar: (toolbars) => toolbars,
|
||||
zoomSpeed: .05,
|
||||
disableKeyboardSupport: false,
|
||||
noResetZoomAfterChange: false,
|
||||
noLimitInitializationSize: false,
|
||||
defaultScale: 1,
|
||||
loop: true,
|
||||
disableMouseZoom: false,
|
||||
};
|
||||
|
||||
private prefixCls: string;
|
||||
private containerWidth: number;
|
||||
private containerHeight: number;
|
||||
private footerHeight: number;
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.prefixCls = 'react-viewer';
|
||||
|
||||
this.state = {
|
||||
visible: false,
|
||||
visibleStart: false,
|
||||
transitionEnd: false,
|
||||
activeIndex: this.props.activeIndex,
|
||||
width: 0,
|
||||
height: 0,
|
||||
top: 15,
|
||||
left: null,
|
||||
rotate: 0,
|
||||
imageWidth: 0,
|
||||
imageHeight: 0,
|
||||
scaleX: this.props.defaultScale,
|
||||
scaleY: this.props.defaultScale,
|
||||
loading: false,
|
||||
loadFailed: false,
|
||||
};
|
||||
|
||||
this.setContainerWidthHeight();
|
||||
this.footerHeight = constants.FOOTER_HEIGHT;
|
||||
}
|
||||
|
||||
setContainerWidthHeight() {
|
||||
this.containerWidth = window.innerWidth;
|
||||
this.containerHeight = window.innerHeight;
|
||||
if (this.props.container) {
|
||||
this.containerWidth = this.props.container.offsetWidth;
|
||||
this.containerHeight = this.props.container.offsetHeight;
|
||||
this.setInlineContainerHeight();
|
||||
}
|
||||
}
|
||||
|
||||
setInlineContainerHeight() {
|
||||
const core = (this.refs['viewerCore'] as HTMLDivElement);
|
||||
if (core) {
|
||||
this.containerHeight = core.offsetHeight;
|
||||
}
|
||||
}
|
||||
|
||||
handleClose = () => {
|
||||
this.props.onClose();
|
||||
}
|
||||
|
||||
startVisible(activeIndex: number) {
|
||||
if (!this.props.container) {
|
||||
document.body.style.overflow = 'hidden';
|
||||
if (document.body.scrollHeight > document.body.clientHeight) {
|
||||
document.body.style.paddingRight = '15px';
|
||||
}
|
||||
}
|
||||
this.setState({
|
||||
visibleStart: true,
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.setState({
|
||||
visible: true,
|
||||
activeIndex,
|
||||
});
|
||||
setTimeout(() => {
|
||||
this.bindEvent();
|
||||
this.loadImg(activeIndex);
|
||||
}, 300);
|
||||
}, 10);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
const core = (this.refs['viewerCore'] as HTMLDivElement);
|
||||
core.addEventListener(
|
||||
'transitionend',
|
||||
this.handleTransitionEnd,
|
||||
false,
|
||||
);
|
||||
// Though onWheel can be directly used on the div "viewerCore", to be able to
|
||||
// prevent default action, a listener is added here instead
|
||||
(this.refs['viewerCore'] as HTMLDivElement).addEventListener(
|
||||
'wheel',
|
||||
this.handleMouseScroll,
|
||||
false,
|
||||
);
|
||||
if (this.containerHeight === 0) {
|
||||
this.setInlineContainerHeight();
|
||||
}
|
||||
this.startVisible(this.state.activeIndex);
|
||||
}
|
||||
|
||||
getImgWidthHeight(imgWidth, imgHeight) {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
let maxWidth = this.containerWidth * 0.8;
|
||||
let maxHeight = (this.containerHeight - this.footerHeight) * 0.8;
|
||||
width = Math.min(maxWidth, imgWidth);
|
||||
height = width / imgWidth * imgHeight;
|
||||
if (height > maxHeight) {
|
||||
height = maxHeight;
|
||||
width = height / imgHeight * imgWidth;
|
||||
}
|
||||
if (this.props.noLimitInitializationSize) {
|
||||
width = imgWidth;
|
||||
height = imgHeight;
|
||||
}
|
||||
return [width, height];
|
||||
}
|
||||
|
||||
loadImgSuccess = (activeImage: ImageDecorator, imgWidth, imgHeight, isNewImage: boolean) => {
|
||||
let realImgWidth = imgWidth;
|
||||
let realImgHeight = imgHeight;
|
||||
if (this.props.defaultSize) {
|
||||
realImgWidth = this.props.defaultSize.width;
|
||||
realImgHeight = this.props.defaultSize.height;
|
||||
}
|
||||
if (activeImage.defaultSize) {
|
||||
realImgWidth = activeImage.defaultSize.width;
|
||||
realImgHeight = activeImage.defaultSize.height;
|
||||
}
|
||||
let [width, height] = this.getImgWidthHeight(realImgWidth, realImgHeight);
|
||||
let left = (this.containerWidth - width) / 2;
|
||||
let top = (this.containerHeight - height - this.footerHeight) / 2;
|
||||
let scaleX = this.props.defaultScale;
|
||||
let scaleY = this.props.defaultScale;
|
||||
if (this.props.noResetZoomAfterChange && isNewImage) {
|
||||
scaleX = this.state.scaleX;
|
||||
scaleY = this.state.scaleY;
|
||||
}
|
||||
this.setState({
|
||||
width: width,
|
||||
height: height,
|
||||
left: left,
|
||||
top: top,
|
||||
imageWidth: imgWidth,
|
||||
imageHeight: imgHeight,
|
||||
loading: false,
|
||||
rotate: 0,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
});
|
||||
}
|
||||
|
||||
loadImg(activeIndex, isNewImage: boolean = false) {
|
||||
let activeImage: ImageDecorator = null;
|
||||
let images = this.props.images || [];
|
||||
if (images.length > 0) {
|
||||
activeImage = images[activeIndex];
|
||||
}
|
||||
let loadComplete = false;
|
||||
let img = new Image();
|
||||
this.setState({
|
||||
activeIndex: activeIndex,
|
||||
loading: true,
|
||||
loadFailed: false,
|
||||
}, () => {
|
||||
img.onload = () => {
|
||||
if (!loadComplete) {
|
||||
this.loadImgSuccess(activeImage, img.width, img.height, isNewImage);
|
||||
}
|
||||
};
|
||||
img.onerror = () => {
|
||||
if (this.props.defaultImg) {
|
||||
this.setState({
|
||||
loadFailed: true,
|
||||
});
|
||||
const deafultImgWidth = this.props.defaultImg.width || this.containerWidth * .5;
|
||||
const defaultImgHeight = this.props.defaultImg.height || this.containerHeight * .5;
|
||||
this.loadImgSuccess(activeImage, deafultImgWidth, defaultImgHeight, isNewImage);
|
||||
} else {
|
||||
this.setState({
|
||||
activeIndex: activeIndex,
|
||||
imageWidth: 0,
|
||||
imageHeight: 0,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
};
|
||||
img.src = activeImage.src;
|
||||
if (img.complete) {
|
||||
loadComplete = true;
|
||||
this.loadImgSuccess(activeImage, img.width, img.height, isNewImage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleChangeImg = (newIndex: number) => {
|
||||
if (!this.props.loop && (newIndex >= this.props.images.length || newIndex < 0)) {
|
||||
return;
|
||||
}
|
||||
if (newIndex >= this.props.images.length) {
|
||||
newIndex = 0;
|
||||
}
|
||||
if (newIndex < 0) {
|
||||
newIndex = this.props.images.length - 1;
|
||||
}
|
||||
if (newIndex === this.state.activeIndex) {
|
||||
return;
|
||||
}
|
||||
if (this.props.onChange) {
|
||||
const activeImage = this.getActiveImage(newIndex);
|
||||
this.props.onChange(activeImage, newIndex);
|
||||
}
|
||||
this.loadImg(newIndex, true);
|
||||
}
|
||||
|
||||
handleChangeImgState = (width, height, top, left) => {
|
||||
this.setState({
|
||||
width: width,
|
||||
height: height,
|
||||
top: top,
|
||||
left: left,
|
||||
});
|
||||
}
|
||||
|
||||
handleDefaultAction = (type: ActionType) => {
|
||||
switch (type) {
|
||||
case ActionType.prev:
|
||||
this.handleChangeImg(this.state.activeIndex - 1);
|
||||
break;
|
||||
case ActionType.next:
|
||||
this.handleChangeImg(this.state.activeIndex + 1);
|
||||
break;
|
||||
case ActionType.zoomIn:
|
||||
let imgCenterXY = this.getImageCenterXY();
|
||||
this.handleZoom(imgCenterXY.x, imgCenterXY.y, 1, this.props.zoomSpeed);
|
||||
break;
|
||||
case ActionType.zoomOut:
|
||||
let imgCenterXY2 = this.getImageCenterXY();
|
||||
this.handleZoom(imgCenterXY2.x, imgCenterXY2.y, -1, this.props.zoomSpeed);
|
||||
break;
|
||||
case ActionType.rotateLeft:
|
||||
this.handleRotate();
|
||||
break;
|
||||
case ActionType.rotateRight:
|
||||
this.handleRotate(true);
|
||||
break;
|
||||
case ActionType.reset:
|
||||
this.loadImg(this.state.activeIndex);
|
||||
break;
|
||||
case ActionType.scaleX:
|
||||
this.handleScaleX(-1);
|
||||
break;
|
||||
case ActionType.scaleY:
|
||||
this.handleScaleY(-1);
|
||||
break;
|
||||
case ActionType.download:
|
||||
this.handleDownload();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
handleAction = (config: ToolbarConfig) => {
|
||||
this.handleDefaultAction(config.actionType);
|
||||
|
||||
if (config.onClick) {
|
||||
const activeImage = this.getActiveImage();
|
||||
config.onClick(activeImage);
|
||||
}
|
||||
}
|
||||
|
||||
handleDownload = () => {
|
||||
const activeImage = this.getActiveImage();
|
||||
if (activeImage.downloadUrl) {
|
||||
location.href = activeImage.downloadUrl;
|
||||
}
|
||||
}
|
||||
|
||||
handleScaleX = (newScale: 1 | -1) => {
|
||||
this.setState({
|
||||
scaleX: this.state.scaleX * newScale,
|
||||
});
|
||||
}
|
||||
|
||||
handleScaleY = (newScale: 1 | -1) => {
|
||||
this.setState({
|
||||
scaleY: this.state.scaleY * newScale,
|
||||
});
|
||||
}
|
||||
|
||||
handleScrollZoom = (targetX, targetY, direct) => {
|
||||
this.handleZoom(targetX, targetY, direct, this.props.zoomSpeed);
|
||||
}
|
||||
|
||||
handleZoom = (targetX, targetY, direct, scale) => {
|
||||
let imgCenterXY = this.getImageCenterXY();
|
||||
let diffX = targetX - imgCenterXY.x;
|
||||
let diffY = targetY - imgCenterXY.y;
|
||||
let top = 0;
|
||||
let left = 0;
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
let scaleX = 0;
|
||||
let scaleY = 0;
|
||||
if (this.state.width === 0) {
|
||||
const [imgWidth, imgHeight] = this.getImgWidthHeight(
|
||||
this.state.imageWidth,
|
||||
this.state.imageHeight,
|
||||
);
|
||||
left = (this.containerWidth - imgWidth) / 2;
|
||||
top = (this.containerHeight - this.footerHeight - imgHeight) / 2;
|
||||
width = this.state.width + imgWidth;
|
||||
height = this.state.height + imgHeight;
|
||||
scaleX = scaleY = 1;
|
||||
} else {
|
||||
let directX = this.state.scaleX > 0 ? 1 : -1;
|
||||
let directY = this.state.scaleY > 0 ? 1 : -1;
|
||||
scaleX = this.state.scaleX + scale * direct * directX;
|
||||
scaleY = this.state.scaleY + scale * direct * directY;
|
||||
if (Math.abs(scaleX) < 0.1 || Math.abs(scaleY) < 0.1) {
|
||||
return;
|
||||
}
|
||||
top = this.state.top + -direct * diffY / this.state.scaleX * scale * directX;
|
||||
left = this.state.left + -direct * diffX / this.state.scaleY * scale * directY;
|
||||
width = this.state.width;
|
||||
height = this.state.height;
|
||||
}
|
||||
this.setState({
|
||||
width: width,
|
||||
scaleX: scaleX,
|
||||
scaleY: scaleY,
|
||||
height: height,
|
||||
top: top,
|
||||
left: left,
|
||||
loading: false,
|
||||
});
|
||||
}
|
||||
|
||||
getImageCenterXY = () => {
|
||||
return {
|
||||
x: this.state.left + this.state.width / 2,
|
||||
y: this.state.top + this.state.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
handleRotate = (isRight: boolean = false) => {
|
||||
this.setState({
|
||||
rotate: this.state.rotate + 90 * (isRight ? 1 : -1),
|
||||
});
|
||||
}
|
||||
|
||||
handleResize = () => {
|
||||
this.setContainerWidthHeight();
|
||||
if (this.props.visible) {
|
||||
let left = (this.containerWidth - this.state.width) / 2;
|
||||
let top = (this.containerHeight - this.state.height - this.footerHeight) / 2;
|
||||
this.setState({
|
||||
left: left,
|
||||
top: top,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
handleKeydown = (e) => {
|
||||
let keyCode = e.keyCode || e.which || e.charCode;
|
||||
let isFeatrue = false;
|
||||
switch (keyCode) {
|
||||
// key: esc
|
||||
case 27:
|
||||
this.props.onClose();
|
||||
isFeatrue = true;
|
||||
break;
|
||||
// key: ←
|
||||
case 37:
|
||||
if (e.ctrlKey) {
|
||||
this.handleDefaultAction(ActionType.rotateLeft);
|
||||
} else {
|
||||
this.handleDefaultAction(ActionType.prev);
|
||||
}
|
||||
isFeatrue = true;
|
||||
break;
|
||||
// key: →
|
||||
case 39:
|
||||
if (e.ctrlKey) {
|
||||
this.handleDefaultAction(ActionType.rotateRight);
|
||||
} else {
|
||||
this.handleDefaultAction(ActionType.next);
|
||||
}
|
||||
isFeatrue = true;
|
||||
break;
|
||||
// key: ↑
|
||||
case 38:
|
||||
this.handleDefaultAction(ActionType.zoomIn);
|
||||
isFeatrue = true;
|
||||
break;
|
||||
// key: ↓
|
||||
case 40:
|
||||
this.handleDefaultAction(ActionType.zoomOut);
|
||||
isFeatrue = true;
|
||||
break;
|
||||
// key: Ctrl + 1
|
||||
case 49:
|
||||
if (e.ctrlKey) {
|
||||
this.loadImg(this.state.activeIndex);
|
||||
isFeatrue = true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (isFeatrue) {
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
handleTransitionEnd = () => {
|
||||
if (!this.state.transitionEnd || this.state.visibleStart) {
|
||||
this.setState({
|
||||
visibleStart: false,
|
||||
transitionEnd: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
bindEvent(remove: boolean = false) {
|
||||
let funcName = 'addEventListener';
|
||||
if (remove) {
|
||||
funcName = 'removeEventListener';
|
||||
}
|
||||
if (!this.props.disableKeyboardSupport) {
|
||||
document[funcName]('keydown', this.handleKeydown, false);
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
this.bindEvent(true);
|
||||
(this.refs['viewerCore'] as HTMLDivElement).removeEventListener(
|
||||
'transitionend',
|
||||
this.handleTransitionEnd,
|
||||
false,
|
||||
);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ViewerProps) {
|
||||
if (!this.props.visible && nextProps.visible) {
|
||||
this.startVisible(nextProps.activeIndex);
|
||||
return;
|
||||
}
|
||||
if (this.props.visible && !nextProps.visible) {
|
||||
this.bindEvent(true);
|
||||
this.handleZoom(
|
||||
this.containerWidth / 2,
|
||||
(this.containerHeight - this.footerHeight) / 2,
|
||||
-1,
|
||||
(this.state.scaleX > 0 ? 1 : -1) * this.state.scaleX - 0.11,
|
||||
);
|
||||
setTimeout(() => {
|
||||
document.body.style.overflow = '';
|
||||
document.body.style.paddingRight = '';
|
||||
this.setState({
|
||||
visible: false,
|
||||
transitionEnd: false,
|
||||
width: 0,
|
||||
height: 0,
|
||||
scaleX: this.props.defaultScale,
|
||||
scaleY: this.props.defaultScale,
|
||||
rotate: 1,
|
||||
imageWidth: 0,
|
||||
imageHeight: 0,
|
||||
loadFailed: false,
|
||||
});
|
||||
}, transitionDuration);
|
||||
return;
|
||||
}
|
||||
if (this.props.activeIndex !== nextProps.activeIndex) {
|
||||
this.handleChangeImg(nextProps.activeIndex);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
handleCanvasMouseDown = e => {
|
||||
this.props.onMaskClick(e);
|
||||
}
|
||||
|
||||
getActiveImage = (activeIndex = undefined) => {
|
||||
let activeImg: ImageDecorator = {
|
||||
src: '',
|
||||
alt: '',
|
||||
downloadUrl: '',
|
||||
};
|
||||
|
||||
let images = this.props.images || [];
|
||||
let realActiveIndex = null;
|
||||
if (activeIndex !== undefined) {
|
||||
realActiveIndex = activeIndex;
|
||||
} else {
|
||||
realActiveIndex = this.state.activeIndex;
|
||||
}
|
||||
if (images.length > 0 && realActiveIndex >= 0) {
|
||||
activeImg = images[realActiveIndex];
|
||||
}
|
||||
|
||||
return activeImg;
|
||||
}
|
||||
|
||||
handleMouseScroll = (e) => {
|
||||
if (this.props.disableMouseZoom) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
let direct: 0 | 1 | -1 = 0;
|
||||
const value = e.deltaY;
|
||||
if (value === 0) {
|
||||
direct = 0;
|
||||
} else {
|
||||
direct = value > 0 ? -1 : 1;
|
||||
}
|
||||
if (direct !== 0) {
|
||||
let x = e.clientX;
|
||||
let y = e.clientY;
|
||||
if (this.props.container) {
|
||||
const containerRect = this.props.container.getBoundingClientRect();
|
||||
x -= containerRect.left;
|
||||
y -= containerRect.top;
|
||||
}
|
||||
this.handleScrollZoom(x, y, direct);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
let activeImg: ImageDecorator = {
|
||||
src: '',
|
||||
alt: '',
|
||||
};
|
||||
|
||||
let zIndex = 1000;
|
||||
|
||||
if (this.props.zIndex) {
|
||||
zIndex = this.props.zIndex;
|
||||
}
|
||||
|
||||
let viewerStryle: React.CSSProperties = {
|
||||
opacity: this.state.visible ? 1 : 0,
|
||||
};
|
||||
|
||||
if (!this.state.visible && this.state.transitionEnd) {
|
||||
viewerStryle.display = 'none';
|
||||
}
|
||||
if (!this.state.visible && this.state.visibleStart) {
|
||||
viewerStryle.display = 'block';
|
||||
}
|
||||
if (this.state.visible && this.state.transitionEnd) {
|
||||
activeImg = this.getActiveImage();
|
||||
}
|
||||
|
||||
let className = `${this.prefixCls} ${this.prefixCls}-transition`;
|
||||
if (this.props.container) {
|
||||
className += ` ${this.prefixCls}-inline`;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref="viewerCore"
|
||||
className={className}
|
||||
style={viewerStryle}
|
||||
>
|
||||
<div className={`${this.prefixCls}-mask`} style={{ zIndex: zIndex }} />
|
||||
{this.props.noClose || (
|
||||
<div
|
||||
className={`${this.prefixCls}-close ${this.prefixCls}-btn`}
|
||||
onClick={this.handleClose}
|
||||
style={{ zIndex: zIndex + 10 }}
|
||||
>
|
||||
<Icon type={ActionType.close} />
|
||||
</div>
|
||||
)}
|
||||
<ViewerCanvas
|
||||
prefixCls={this.prefixCls}
|
||||
imgSrc={this.state.loadFailed ? this.props.defaultImg.src || activeImg.src : activeImg.src}
|
||||
visible={this.props.visible}
|
||||
width={this.state.width}
|
||||
height={this.state.height}
|
||||
top={this.state.top}
|
||||
left={this.state.left}
|
||||
rotate={this.state.rotate}
|
||||
onChangeImgState={this.handleChangeImgState}
|
||||
onResize={this.handleResize}
|
||||
zIndex={zIndex + 5}
|
||||
scaleX={this.state.scaleX}
|
||||
scaleY={this.state.scaleY}
|
||||
loading={this.state.loading}
|
||||
drag={this.props.drag}
|
||||
container={this.props.container}
|
||||
onCanvasMouseDown={this.handleCanvasMouseDown}
|
||||
/>
|
||||
{this.props.noFooter || (
|
||||
<div className={`${this.prefixCls}-footer`} style={{ zIndex: zIndex + 5 }}>
|
||||
{this.props.noToolbar || (
|
||||
<ViewerToolbar
|
||||
prefixCls={this.prefixCls}
|
||||
onAction={this.handleAction}
|
||||
alt={activeImg.alt}
|
||||
width={this.state.imageWidth}
|
||||
height={this.state.imageHeight}
|
||||
attribute={this.props.attribute}
|
||||
zoomable={this.props.zoomable}
|
||||
rotatable={this.props.rotatable}
|
||||
scalable={this.props.scalable}
|
||||
changeable={this.props.changeable}
|
||||
downloadable={this.props.downloadable}
|
||||
noImgDetails={this.props.noImgDetails}
|
||||
toolbars={this.props.customToolbar(defaultToolbars)}
|
||||
/>
|
||||
)}
|
||||
{this.props.noNavbar || (
|
||||
<ViewerNav
|
||||
prefixCls={this.prefixCls}
|
||||
images={this.props.images}
|
||||
activeIndex={this.state.activeIndex}
|
||||
onChangeImg={this.handleChangeImg}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
1015
src/ViewerCore.tsx
1015
src/ViewerCore.tsx
File diff suppressed because it is too large
Load Diff
@ -8,39 +8,35 @@ export interface ViewerNavProps {
|
||||
onChangeImg: (index: number) => void;
|
||||
}
|
||||
|
||||
export default class ViewerNav extends React.Component<ViewerNavProps, any> {
|
||||
static defaultProps = {
|
||||
activeIndex: 0,
|
||||
};
|
||||
export default function ViewerNav(props: ViewerNavProps) {
|
||||
const { activeIndex = 0 } = props;
|
||||
|
||||
handleChangeImg = (newIndex) => {
|
||||
if (this.props.activeIndex === newIndex) {
|
||||
function handleChangeImg(newIndex) {
|
||||
if (activeIndex === newIndex) {
|
||||
return;
|
||||
}
|
||||
this.props.onChangeImg(newIndex);
|
||||
props.onChangeImg(newIndex);
|
||||
}
|
||||
|
||||
render() {
|
||||
let marginLeft = `calc(50% - ${this.props.activeIndex + 1} * 31px)`;
|
||||
let listStyle = {
|
||||
marginLeft: marginLeft,
|
||||
};
|
||||
let marginLeft = `calc(50% - ${activeIndex + 1} * 31px)`;
|
||||
let listStyle = {
|
||||
marginLeft: marginLeft,
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={`${this.props.prefixCls}-navbar`}>
|
||||
<ul className={`${this.props.prefixCls}-list ${this.props.prefixCls}-list-transition`} style={listStyle}>
|
||||
{this.props.images.map((item, index) =>
|
||||
<li
|
||||
key={index}
|
||||
className={index === this.props.activeIndex ? 'active' : ''}
|
||||
onClick={() => { this.handleChangeImg(index); }}
|
||||
>
|
||||
<img src={item.src} alt={item.alt} />
|
||||
</li>,
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<div className={`${props.prefixCls}-navbar`}>
|
||||
<ul className={`${props.prefixCls}-list ${props.prefixCls}-list-transition`} style={listStyle}>
|
||||
{props.images.map((item, index) =>
|
||||
<li
|
||||
key={index}
|
||||
className={index === activeIndex ? 'active' : ''}
|
||||
onClick={() => { handleChangeImg(index); }}
|
||||
>
|
||||
<img src={item.src} alt={item.alt} />
|
||||
</li>,
|
||||
)
|
||||
}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -67,17 +67,12 @@ function deleteToolbarFromKey(toolbars: ToolbarConfig[], keys: string[]) {
|
||||
return targetToolbar;
|
||||
}
|
||||
|
||||
export default class ViewerToolbar extends React.Component<ViewerToolbarProps, any> {
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
export default function ViewerToolbar(props: ViewerToolbarProps) {
|
||||
function handleAction(config: ToolbarConfig) {
|
||||
props.onAction(config);
|
||||
}
|
||||
|
||||
handleAction(config: ToolbarConfig) {
|
||||
this.props.onAction(config);
|
||||
}
|
||||
|
||||
renderAction = (config: ToolbarConfig) => {
|
||||
function renderAction(config: ToolbarConfig) {
|
||||
let content = null;
|
||||
// default toolbar
|
||||
if (typeof ActionType[config.actionType] !== 'undefined') {
|
||||
@ -90,49 +85,46 @@ export default class ViewerToolbar extends React.Component<ViewerToolbarProps, a
|
||||
return (
|
||||
<li
|
||||
key={config.key}
|
||||
className={`${this.props.prefixCls}-btn`}
|
||||
onClick={() => {this.handleAction(config); }}
|
||||
className={`${props.prefixCls}-btn`}
|
||||
onClick={() => {handleAction(config); }}
|
||||
data-key={config.key}
|
||||
>
|
||||
{content}
|
||||
</li>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
let attributeNode = this.props.attribute ? (
|
||||
<p className={`${this.props.prefixCls}-attribute`}>
|
||||
{this.props.alt && `${this.props.alt}`}
|
||||
{this.props.noImgDetails || <span className={`${this.props.prefixCls}-img-details`}>
|
||||
{`(${this.props.width} x ${this.props.height})`}
|
||||
</span>}
|
||||
</p>
|
||||
) : null;
|
||||
let toolbars = this.props.toolbars;
|
||||
if (!this.props.zoomable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['zoomIn', 'zoomOut']);
|
||||
}
|
||||
if (!this.props.changeable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['prev', 'next']);
|
||||
}
|
||||
if (!this.props.rotatable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['rotateLeft', 'rotateRight']);
|
||||
}
|
||||
if (!this.props.scalable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['scaleX', 'scaleY']);
|
||||
}
|
||||
if (!this.props.downloadable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['download']);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
{attributeNode}
|
||||
<ul className={`${this.props.prefixCls}-toolbar`}>
|
||||
{toolbars.map(item => {
|
||||
return this.renderAction(item);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
let attributeNode = props.attribute ? (
|
||||
<p className={`${props.prefixCls}-attribute`}>
|
||||
{props.alt && `${props.alt}`}
|
||||
{props.noImgDetails || <span className={`${props.prefixCls}-img-details`}>
|
||||
{`(${props.width} x ${props.height})`}
|
||||
</span>}
|
||||
</p>
|
||||
) : null;
|
||||
let toolbars = props.toolbars;
|
||||
if (!props.zoomable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['zoomIn', 'zoomOut']);
|
||||
}
|
||||
if (!props.changeable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['prev', 'next']);
|
||||
}
|
||||
if (!props.rotatable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['rotateLeft', 'rotateRight']);
|
||||
}
|
||||
if (!props.scalable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['scaleX', 'scaleY']);
|
||||
}
|
||||
if (!props.downloadable) {
|
||||
toolbars = deleteToolbarFromKey(toolbars, ['download']);
|
||||
}
|
||||
return (
|
||||
<div>
|
||||
{attributeNode}
|
||||
<ul className={`${props.prefixCls}-toolbar`}>
|
||||
{toolbars.map(item => {
|
||||
return renderAction(item);
|
||||
})}
|
||||
</ul>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user