mirror of
https://github.com/infeng/react-viewer.git
synced 2025-12-08 17:36:40 +00:00
Merge pull request #26 from KapMug/master
Added new options for redering
This commit is contained in:
commit
d720fbf7dd
35
README.md
35
README.md
@ -46,21 +46,26 @@ class App extends React.Component<any, any> {
|
||||
|
||||
## Props
|
||||
|
||||
| props | type | default | description | required |
|
||||
|-------------|--------------|---------|-----------------------------|----------|
|
||||
| visible | string | false | Viewer visible | true |
|
||||
| onClose | string | - | Specify a function that will be called when Visible close | true |
|
||||
| images | [ImageDecorator](#ImageDecorator)[] | [] | image source array | true |
|
||||
| activeIndex | number | 0 | active image index | false |
|
||||
| zIndex | number | 1000 | Viewer css z-index | false |
|
||||
| container | HTMLElement | null | set parent node(inline mode) | false |
|
||||
| drag | boolean | true | whether to drag image | false |
|
||||
| attribute | boolean | true | whether to show image attribute | false |
|
||||
| zoomable | boolean | true | whether to show 'zoom' button | false |
|
||||
| rotatable | boolean | true | whether to show 'rotate' button | false |
|
||||
| scalable | boolean | true | whether to show 'scale' button | false |
|
||||
| onMaskClick | (e) => void | - | callback function when mask is clicked | false |
|
||||
| download | boolean | false | whether to show 'download' | false |
|
||||
| props | type | default | description | required |
|
||||
|--------------|--------------|---------|-----------------------------|----------|
|
||||
| visible | string | false | Viewer visible | true |
|
||||
| onClose | string | - | Specify a function that will be called when Visible close | true |
|
||||
| images | [ImageDecorator](#ImageDecorator)[] | [] | image source array | true |
|
||||
| activeIndex | number | 0 | active image index | false |
|
||||
| zIndex | number | 1000 | Viewer css z-index | false |
|
||||
| container | HTMLElement | null | set parent node(inline mode) | false |
|
||||
| drag | boolean | true | whether to drag image | false |
|
||||
| attribute | boolean | true | whether to show image attribute | false |
|
||||
| zoomable | boolean | true | whether to show 'zoom' button | false |
|
||||
| rotatable | boolean | true | whether to show 'rotate' button | false |
|
||||
| scalable | boolean | true | whether to show 'scale' button | false |
|
||||
| onMaskClick | (e) => void | - | callback function when mask is clicked | false |
|
||||
| download | boolean | false | whether to show 'download' | false |
|
||||
| noClose | boolean | false | to not render close button | false |
|
||||
| noNavbar | boolean | false | to not render the navbar | false |
|
||||
| noToolbar | boolean | false | to not render the toolbar | false |
|
||||
| noImgDetails | boolean | false | to not render image detail (WxH) | false |
|
||||
| noFooter | boolean | false | to not render the entire footer | false |
|
||||
|
||||
### ImageDecorator
|
||||
|
||||
|
||||
@ -51,12 +51,10 @@ export default class Viewer extends React.Component<ViewerProps, any> {
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
if (this.props.visible) {
|
||||
if (this.props.visible && this.props.onClose) {
|
||||
this.props.onClose();
|
||||
this.removeViewer();
|
||||
} else {
|
||||
this.removeViewer();
|
||||
}
|
||||
this.removeViewer();
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ViewerProps) {
|
||||
|
||||
@ -119,20 +119,24 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
(this.refs['viewerCore'] as HTMLDivElement).addEventListener('transitionend', this.handleTransitionEnd, false);
|
||||
(this.refs['viewerCore'] as HTMLDivElement).addEventListener(
|
||||
'transitionend',
|
||||
this.handleTransitionEnd,
|
||||
false
|
||||
);
|
||||
this.startVisible(this.state.activeIndex);
|
||||
}
|
||||
|
||||
getImgWidthHeight(imgWidth, imgHeight) {
|
||||
let width = 0;
|
||||
let height = 0;
|
||||
let maxWidth = this.containerWidth * .8;
|
||||
let maxHeight = (this.containerHeight - this.footerHeight) * .8;
|
||||
let maxWidth = this.containerWidth * 0.8;
|
||||
let maxHeight = (this.containerHeight - this.footerHeight) * 0.8;
|
||||
width = Math.min(maxWidth, imgWidth);
|
||||
height = (width / imgWidth) * imgHeight;
|
||||
height = width / imgWidth * imgHeight;
|
||||
if (height > maxHeight) {
|
||||
height = maxHeight;
|
||||
width = (height / imgHeight) * imgWidth;
|
||||
width = height / imgHeight * imgWidth;
|
||||
}
|
||||
return [width, height];
|
||||
}
|
||||
@ -151,13 +155,13 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
width: 0,
|
||||
height: 0,
|
||||
left: 0,
|
||||
top: 0,
|
||||
top: 0,
|
||||
rotate: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
loading: true,
|
||||
});
|
||||
}else {
|
||||
} else {
|
||||
this.setState({
|
||||
activeIndex: activeIndex,
|
||||
loading: true,
|
||||
@ -176,16 +180,16 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
let imgCenterXY = this.getImageCenterXY();
|
||||
this.handleZoom(imgCenterXY.x, imgCenterXY.y, 1, 1);
|
||||
}, 50);
|
||||
}else {
|
||||
const [ width, height ] = this.getImgWidthHeight(imgWidth, imgHeight);
|
||||
let left = ( this.containerWidth - width ) / 2;
|
||||
} else {
|
||||
const [width, height] = this.getImgWidthHeight(imgWidth, imgHeight);
|
||||
let left = (this.containerWidth - width) / 2;
|
||||
let top = (this.containerHeight - height - this.footerHeight) / 2;
|
||||
this.setState({
|
||||
activeIndex: activeIndex,
|
||||
width: width,
|
||||
height: height,
|
||||
left: left,
|
||||
top: top,
|
||||
top: top,
|
||||
imageWidth: imgWidth,
|
||||
imageHeight: imgHeight,
|
||||
loading: false,
|
||||
@ -237,11 +241,11 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
break;
|
||||
case ActionType.zoomIn:
|
||||
let imgCenterXY = this.getImageCenterXY();
|
||||
this.handleZoom(imgCenterXY.x, imgCenterXY.y, 1, .05);
|
||||
this.handleZoom(imgCenterXY.x, imgCenterXY.y, 1, 0.05);
|
||||
break;
|
||||
case ActionType.zoomOut:
|
||||
let imgCenterXY2 = this.getImageCenterXY();
|
||||
this.handleZoom(imgCenterXY2.x, imgCenterXY2.y, -1, .05);
|
||||
this.handleZoom(imgCenterXY2.x, imgCenterXY2.y, -1, 0.05);
|
||||
break;
|
||||
case ActionType.rotateLeft:
|
||||
this.handleRotate();
|
||||
@ -271,7 +275,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
if (activeImage.downloadUrl) {
|
||||
window.open(activeImage.downloadUrl);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
handleScaleX(newScale: 1 | -1) {
|
||||
this.setState({
|
||||
@ -298,14 +302,17 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
let scaleX = 0;
|
||||
let scaleY = 0;
|
||||
if (this.state.width === 0) {
|
||||
const [ imgWidth, imgHeight ] = this.getImgWidthHeight(this.state.imageWidth, this.state.imageHeight);
|
||||
const [imgWidth, imgHeight] = this.getImgWidthHeight(
|
||||
this.state.imageWidth,
|
||||
this.state.imageHeight
|
||||
);
|
||||
reset = true;
|
||||
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 {
|
||||
} else {
|
||||
let directX = this.state.scaleX > 0 ? 1 : -1;
|
||||
let directY = this.state.scaleY > 0 ? 1 : -1;
|
||||
scaleX = this.state.scaleX + scale * direct * directX;
|
||||
@ -331,8 +338,8 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
|
||||
getImageCenterXY() {
|
||||
return {
|
||||
x: (this.state.left + this.state.width / 2),
|
||||
y: (this.state.top + this.state.height / 2),
|
||||
x: this.state.left + this.state.width / 2,
|
||||
y: this.state.top + this.state.height / 2,
|
||||
};
|
||||
}
|
||||
|
||||
@ -345,14 +352,14 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
handleResize() {
|
||||
this.setContainerWidthHeight();
|
||||
if (this.props.visible) {
|
||||
const [ width, height ] = this.getImgWidthHeight(this.state.imageWidth, this.state.imageHeight);
|
||||
let left = ( this.containerWidth - width ) / 2;
|
||||
const [width, height] = this.getImgWidthHeight(this.state.imageWidth, this.state.imageHeight);
|
||||
let left = (this.containerWidth - width) / 2;
|
||||
let top = (this.containerHeight - height - this.footerHeight) / 2;
|
||||
this.setState({
|
||||
width: width,
|
||||
height: height,
|
||||
left: left,
|
||||
top: top,
|
||||
top: top,
|
||||
rotate: 0,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
@ -373,7 +380,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
case 37:
|
||||
if (e.ctrlKey) {
|
||||
this.handleAction(ActionType.rotateLeft);
|
||||
}else {
|
||||
} else {
|
||||
this.handleAction(ActionType.prev);
|
||||
}
|
||||
isFeatrue = true;
|
||||
@ -382,7 +389,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
case 39:
|
||||
if (e.ctrlKey) {
|
||||
this.handleAction(ActionType.rotateRight);
|
||||
}else {
|
||||
} else {
|
||||
this.handleAction(ActionType.next);
|
||||
}
|
||||
isFeatrue = true;
|
||||
@ -412,14 +419,14 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
}
|
||||
}
|
||||
|
||||
handleTransitionEnd = (e) => {
|
||||
handleTransitionEnd = e => {
|
||||
if (!this.state.transitionEnd || this.state.visibleStart) {
|
||||
this.setState({
|
||||
visibleStart: false,
|
||||
transitionEnd: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
bindEvent(remove: boolean = false) {
|
||||
let funcName = 'addEventListener';
|
||||
@ -431,7 +438,11 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
|
||||
componentWillUnmount() {
|
||||
this.bindEvent(true);
|
||||
(this.refs['viewerCore'] as HTMLDivElement).removeEventListener('transitionend', this.handleTransitionEnd, false);
|
||||
(this.refs['viewerCore'] as HTMLDivElement).removeEventListener(
|
||||
'transitionend',
|
||||
this.handleTransitionEnd,
|
||||
false
|
||||
);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: ViewerProps) {
|
||||
@ -445,7 +456,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
this.containerWidth / 2,
|
||||
(this.containerHeight - this.footerHeight) / 2,
|
||||
-1,
|
||||
(this.state.scaleX > 0 ? 1 : -1) * this.state.scaleX - 0.11,
|
||||
(this.state.scaleX > 0 ? 1 : -1) * this.state.scaleX - 0.11
|
||||
);
|
||||
setTimeout(() => {
|
||||
document.body.style.overflow = '';
|
||||
@ -467,7 +478,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
|
||||
handleCanvasMouseDown = e => {
|
||||
this.props.onMaskClick(e);
|
||||
}
|
||||
};
|
||||
|
||||
getActiveImage = () => {
|
||||
let activeImg: ImageDecorator = {
|
||||
@ -482,7 +493,7 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
}
|
||||
|
||||
return activeImg;
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
let activeImg: ImageDecorator = {
|
||||
@ -516,60 +527,65 @@ export default class ViewerCore extends React.Component<ViewerProps, ViewerCoreS
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
ref="viewerCore"
|
||||
className={className}
|
||||
style={viewerStryle}
|
||||
>
|
||||
<div className={`${this.prefixCls}-mask`} style={{zIndex: zIndex}}></div>
|
||||
<div
|
||||
className={`${this.prefixCls}-close ${this.prefixCls}-btn`}
|
||||
onClick={this.handleClose.bind(this)}
|
||||
style={{zIndex: zIndex + 10}}
|
||||
>
|
||||
<Icon type={ActionType.close}/>
|
||||
</div>
|
||||
<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.bind(this)}
|
||||
style={{ zIndex: zIndex + 10 }}
|
||||
>
|
||||
<Icon type={ActionType.close} />
|
||||
</div>
|
||||
)}
|
||||
<ViewerCanvas
|
||||
prefixCls={this.prefixCls}
|
||||
imgSrc={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}
|
||||
onZoom={this.handleZoom}
|
||||
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}
|
||||
prefixCls={this.prefixCls}
|
||||
imgSrc={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}
|
||||
onZoom={this.handleZoom}
|
||||
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}
|
||||
/>
|
||||
<div className={`${this.prefixCls}-footer`} style={{zIndex: zIndex + 5}}>
|
||||
<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={true}
|
||||
downloadable={this.props.downloadable}
|
||||
/>
|
||||
<ViewerNav
|
||||
prefixCls={this.prefixCls}
|
||||
images={this.props.images}
|
||||
activeIndex={this.state.activeIndex}
|
||||
onChangeImg={this.handleChangeImg}
|
||||
/>
|
||||
</div>
|
||||
{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={true}
|
||||
downloadable={this.props.downloadable}
|
||||
noImgDetails={this.props.noImgDetails}
|
||||
/>
|
||||
)}
|
||||
{this.props.noNavbar || (
|
||||
<ViewerNav
|
||||
prefixCls={this.prefixCls}
|
||||
images={this.props.images}
|
||||
activeIndex={this.state.activeIndex}
|
||||
onChangeImg={this.handleChangeImg}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -31,6 +31,21 @@ interface ViewerProps {
|
||||
onMaskClick?: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
/** 是否显示下载按钮 */
|
||||
downloadable?: boolean;
|
||||
|
||||
// no render close button
|
||||
noClose?: boolean;
|
||||
|
||||
// no render image details
|
||||
noImgDetails?: boolean;
|
||||
|
||||
// no render navbar
|
||||
noNavbar?: boolean;
|
||||
|
||||
// no render toolbar
|
||||
noToolbar?: boolean;
|
||||
|
||||
// no render footer
|
||||
noFooter?: boolean;
|
||||
}
|
||||
|
||||
export default ViewerProps;
|
||||
|
||||
@ -13,6 +13,7 @@ export interface ViewerToolbarProps {
|
||||
scalable: boolean;
|
||||
changeable: boolean;
|
||||
downloadable: boolean;
|
||||
noImgDetails: boolean;
|
||||
}
|
||||
|
||||
export default class ViewerToolbar extends React.Component<ViewerToolbarProps, any> {
|
||||
@ -28,7 +29,8 @@ export default class ViewerToolbar extends React.Component<ViewerToolbarProps, a
|
||||
render() {
|
||||
let attributeNode = this.props.attribute ? (
|
||||
<p className={`${this.props.prefixCls}-attribute`}>
|
||||
{`${this.props.alt}(${this.props.width} x ${this.props.height})`}
|
||||
{this.props.alt && `${this.props.alt}`}
|
||||
{this.props.noImgDetails || `(${this.props.width} x ${this.props.height})`}
|
||||
</p>
|
||||
) : null;
|
||||
let featureNodeArr = [];
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user