Skip to content

Commit 652e10f

Browse files
committed
feat: add ErrorIcon/Preview use Image
1 parent a1b478d commit 652e10f

File tree

5 files changed

+88
-69
lines changed

5 files changed

+88
-69
lines changed

src/assets/img-load-error.svg

Lines changed: 1 addition & 0 deletions
Loading

src/lib/Image/index.jsx

Lines changed: 29 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ import './style.scss'
44
import LoadingIcon from '../LoadingIcon'
55
import { CanUseIntersecion, observe, unobserve } from './observer'
66
import { PreviewApi } from '../ImgPreview'
7-
import picNull from '../../assets/pic_null.png'
8-
import picError from '../../assets/pic_loading_fail.png'
7+
import ErrorIcon from '../ErrorIcon'
98
export default class ReactImage extends React.PureComponent {
109
static propTypes = {
1110
/** Component Style */
@@ -26,6 +25,8 @@ export default class ReactImage extends React.PureComponent {
2625
imgProps: PropTypes.object,
2726
/** Can it be previewed */
2827
preview: PropTypes.bool,
28+
/** show mask when hover */
29+
mask: PropTypes.bool,
2930
/** onDelete callback, param: src, it will display an icon on right corner when hover */
3031
onDelete: PropTypes.func,
3132
/** Image onError callback, param: src */
@@ -40,7 +41,8 @@ export default class ReactImage extends React.PureComponent {
4041
width: 100,
4142
group: 'default',
4243
objectFit: 'cover',
43-
preview: true
44+
preview: true,
45+
mask: true
4446
}
4547

4648
constructor() {
@@ -54,15 +56,6 @@ export default class ReactImage extends React.PureComponent {
5456
loadObserve: !CanUseIntersecion // InterseciontObserver, 监听图片是否出现在viewport
5557
}
5658

57-
get url() {
58-
const { src } = this.props
59-
return src === ''
60-
? picNull
61-
: this.state.isError
62-
? picError
63-
: src
64-
}
65-
6659
get style() {
6760
const { height, width } = this.props
6861
return {
@@ -73,15 +66,16 @@ export default class ReactImage extends React.PureComponent {
7366
}
7467

7568
get imgStyle() {
69+
const { isLoading, isError } = this.state
7670
const ret = {
7771
objectFit: this.props.objectFit
7872
}
7973
const { imgProps } = this.props
80-
if(imgProps && imgProps.style) {
74+
if (imgProps && imgProps.style) {
8175
Object.assign(ret, imgProps.style)
8276
}
8377
Object.assign(ret, {
84-
display: this.state.isLoading ? 'none' : ''
78+
display: (isLoading || isError) ? 'none' : ''
8579
})
8680
return ret
8781
}
@@ -98,18 +92,19 @@ export default class ReactImage extends React.PureComponent {
9892
onLoad = () => {
9993
const { onLoad, src } = this.props
10094
this.setState({
101-
isLoading: false
95+
isLoading: false,
96+
isError: false
10297
})
10398
onLoad && onLoad(src)
10499
}
105100

106101
onClickHandler = () => {
107102
const { isLoading, isError } = this.state
108-
const {
109-
src, group, preview, onClick
110-
} = this.props
111-
if(preview && src && !isLoading && !isError) {
112-
const dom = document.querySelectorAll(`.mask-img[data-img-group="${group}"]`)
103+
const { src, group, preview, onClick } = this.props
104+
if (preview && src && !isLoading && !isError) {
105+
const dom = document.querySelectorAll(
106+
`.mask-img[data-img-group="${group}"]`
107+
)
113108
const list = Array.from(dom).map(each => each.dataset.imgSrc)
114109
PreviewApi.preview(src, list)
115110
}
@@ -131,44 +126,44 @@ export default class ReactImage extends React.PureComponent {
131126

132127
render() {
133128
// const { width, height } = this.props
134-
const {
135-
group, imgProps, onDelete, src
136-
} = this.props
137-
const { isLoading, loadObserve } = this.state
129+
const { group, imgProps, onDelete, src, height, preview, className, mask } = this.props
130+
const { isLoading, loadObserve, isError } = this.state
138131

139132
return (
140133
<span
141-
className={['mask-img', this.props.className].join(
142-
' '
143-
)}
144-
data-img-group={group}
145-
data-img-src={this.url}
134+
className={['mask-img', mask ? 'mask' : '', className].join(' ')}
135+
data-img-group={preview ? group : null}
136+
data-img-src={preview ? src : null}
146137
style={this.style}
147138
ref={this.refDom}
148139
>
149-
{this.props.onDelete ? (
140+
{onDelete ? (
150141
<span className="delete-img">
151-
<i className="react-image-icon" style={{display: 'inline-block'}} onClick={() => onDelete(src)}>
142+
<i
143+
className="react-image-icon"
144+
style={{ display: 'inline-block' }}
145+
onClick={() => onDelete(src)}
146+
>
152147
&#xe904;
153148
</i>
154149
</span>
155150
) : null}
156151
{loadObserve && (
157152
<img
158153
{...imgProps}
159-
src={this.url}
154+
src={src}
160155
onError={this.onError}
161156
onLoad={this.onLoad}
162157
onClick={this.onClickHandler}
163158
style={this.imgStyle}
164159
/>
165160
)}
166161
{isLoading && (
167-
<div className="mask-loading">
162+
<div className="mask-loading" style={{minHeight: `${height || 100}px`}}>
168163
<LoadingIcon size="sm" />
169-
<span className="mask-loading-text">Loading</span>
170164
</div>
171165
)}
166+
{isError && <ErrorIcon style={{ width: '100%', height: '100%' }} />}
172167
</span>
173168
)
174169
}

src/lib/Image/style.scss

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,11 @@
66
position: relative;
77
flex-shrink: 0;
88
&:hover {
9-
&:after {
10-
background: rgba(0, 0, 0, 0.1);
11-
}
129
.delete-img {
1310
opacity: 1;
1411
}
1512
}
16-
&:after {
13+
&.mask::after {
1714
content: '';
1815
pointer-events: none;
1916
position: absolute;
@@ -23,6 +20,9 @@
2320
bottom: -2px;
2421
transition: background 0.1s ease;
2522
background: transparent;
23+
&:hover::after {
24+
background: rgba(0, 0, 0, 0.1);
25+
}
2626
}
2727
img {
2828
width: 100%;

src/lib/ImgPreview/ImgPreview.jsx

Lines changed: 50 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@ import React from 'react'
22
// import { reaction } from 'mobx'
33
import ReactDOM from 'react-dom'
44
import LoadingIcon from '../LoadingIcon'
5+
import ErrorIcon from '../ErrorIcon'
6+
import Image from '../Image'
57
// import Store from './imgViewStore'
68
function find(list, arg) {
79
return list.findIndex(each => each === arg)
@@ -23,7 +25,8 @@ export default class ImgPpreview extends React.PureComponent {
2325
current: 0,
2426
open: false,
2527
changed: false,
26-
loaded: false
28+
loaded: false,
29+
error: false
2730
}
2831

2932
/**
@@ -41,15 +44,15 @@ export default class ImgPpreview extends React.PureComponent {
4144
exportPreview = (current, list) => {
4245
let l = this.state.images
4346
let c = current
44-
if(list) {
47+
if (list) {
4548
l = list
4649
}
4750
// 如果为number型则直接设置
48-
if(typeof c !== 'number') {
51+
if (typeof c !== 'number') {
4952
// 否则调用find方法找下标
5053
// 如果不存在则设为images
5154
const idx = find(l, c)
52-
if(idx === -1) {
55+
if (idx === -1) {
5356
l = [c]
5457
c = 0
5558
} else {
@@ -73,11 +76,11 @@ export default class ImgPpreview extends React.PureComponent {
7376
e.stopPropagation()
7477
e.preventDefault()
7578
const { keyCode } = e
76-
if(keyCode === 27) {
79+
if (keyCode === 27) {
7780
this.hide()
78-
} else if(keyCode === 37) {
81+
} else if (keyCode === 37) {
7982
this.prev()
80-
} else if(keyCode === 39) {
83+
} else if (keyCode === 39) {
8184
this.next()
8285
}
8386
}
@@ -90,10 +93,10 @@ export default class ImgPpreview extends React.PureComponent {
9093
*/
9194
hideHandle = e => {
9295
const { target } = e
93-
if(
94-
target === this.$close
95-
|| target === this.$el
96-
|| target === this.$footer
96+
if (
97+
target === this.$close ||
98+
target === this.$el ||
99+
target === this.$footer
97100
) {
98101
this.hide()
99102
}
@@ -191,7 +194,7 @@ export default class ImgPpreview extends React.PureComponent {
191194
*/
192195
mouseDownHandle = e => {
193196
e.stopPropagation()
194-
if(e.button !== 0) {
197+
if (e.button !== 0) {
195198
return
196199
}
197200
const container = this.$el
@@ -240,7 +243,7 @@ export default class ImgPpreview extends React.PureComponent {
240243
// 放大缩小功能
241244
// const delta = e.wheelDelta ? e.wheelDelta : -(e.detail || 0)
242245
let { scale } = this.state
243-
if(-e.deltaY < 0) {
246+
if (-e.deltaY < 0) {
244247
// 放大
245248
scale *= 1.2
246249
} else {
@@ -263,12 +266,13 @@ export default class ImgPpreview extends React.PureComponent {
263266
const width = img.naturalWidth
264267
let scale = 1
265268
const windwoWidth = (window.innerWidth * 3) / 4
266-
if(width > windwoWidth) {
269+
if (width > windwoWidth) {
267270
scale = windwoWidth / width
268271
}
269272
this.setState(
270273
{
271-
loaded: true
274+
loaded: true,
275+
error: false
272276
},
273277
() => {
274278
setTimeout(() => {
@@ -280,6 +284,14 @@ export default class ImgPpreview extends React.PureComponent {
280284
)
281285
}
282286

287+
imgOnError = e => {
288+
console.log('onError');
289+
this.setState({
290+
loaded: true,
291+
error: true
292+
})
293+
}
294+
283295
get scaleFixed() {
284296
return +this.state.scale.toFixed(2)
285297
}
@@ -316,6 +328,7 @@ export default class ImgPpreview extends React.PureComponent {
316328

317329
get imgListStyle() {
318330
return {
331+
display: 'flex',
319332
transform: `translate3d(-${this.state.current * 50}px, 0, 0)`,
320333
transition: 'transform .3s linear'
321334
}
@@ -329,6 +342,7 @@ export default class ImgPpreview extends React.PureComponent {
329342
mouseWheelHandle,
330343
mouseDownHandle,
331344
imgOnLoad,
345+
imgOnError,
332346
imgSty,
333347
prev,
334348
next,
@@ -361,16 +375,20 @@ export default class ImgPpreview extends React.PureComponent {
361375
<img
362376
className={[
363377
'img-viewer-current',
364-
state.loaded ? '' : 'dis-none'
378+
state.loaded && !state.error ? '' : 'dis-none'
365379
].join(' ')}
366380
onMouseDown={mouseDownHandle}
367381
onLoad={imgOnLoad}
382+
onError={imgOnError}
368383
src={currentImg}
369384
alt=""
370385
draggable="false"
371386
style={imgSty}
372387
/>
373388
{!state.loaded && <LoadingIcon />}
389+
{state.error && (
390+
<ErrorIcon style={{ width: '300px', height: '300px' }} />
391+
)}
374392
</React.Fragment>
375393
)}
376394
<div
@@ -405,12 +423,22 @@ export default class ImgPpreview extends React.PureComponent {
405423
<div className="img-viewer-list-inner">
406424
<div style={this.imgListStyle}>
407425
{state.images.map((src, idx) => (
408-
<img
409-
className={state.current === idx ? 'active' : ''}
410-
src={src}
411-
key={idx}
426+
<div
412427
onClick={() => this.change(idx)}
413-
/>
428+
key={idx}
429+
className={[
430+
'img-viewer-list-item',
431+
state.current === idx ? 'active' : ''
432+
].join(' ')}
433+
>
434+
<Image
435+
width="50"
436+
height="50"
437+
preview={false}
438+
mask={false}
439+
src={src}
440+
/>
441+
</div>
414442
))}
415443
</div>
416444
</div>
@@ -425,7 +453,7 @@ ImgPpreview.newInstance = function newNotificationInstance(callback) {
425453
const div = document.createElement('div')
426454
let called = false
427455
function ref(ins) {
428-
if(called) {
456+
if (called) {
429457
return
430458
}
431459
called = true

src/lib/ImgPreview/style.scss

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -78,18 +78,13 @@
7878
overflow: auto;
7979
background: rgba(0, 0, 0, 0.7);
8080
overflow: hidden;
81-
img {
82-
// border: 2px solid transparent;
83-
object-fit: cover;
84-
width: 50px;
81+
.img-viewer-list-item {
8582
opacity: .5;
86-
height: 50px;
87-
box-sizing: border-box;
88-
border: 1px solid transparent;
89-
// margin: 0 5px;
83+
&:hover {
84+
opacity: 1;
85+
}
9086
&.active {
9187
opacity: 1;
92-
border-color: #fff;
9388
}
9489
}
9590
}

0 commit comments

Comments
 (0)