1
+ import React , { Component } from 'react' ;
2
+ import PropTypes from 'prop-types' ;
3
+
4
+ import mapColors from './mapColorsWorker' ;
5
+ import emojify from './emojifyWorker' ;
6
+
7
+ /**
8
+ * @class EmojifyImageCustom
9
+ **/
10
+ export default class EmojifyImageCustom extends Component {
11
+
12
+ _remap ( emojis ) {
13
+ mapColors ( emojis ) . then ( mapping => {
14
+ this . setState ( { mapping, isMappingEmojis : false } ) ;
15
+ } ) ;
16
+ }
17
+
18
+ _redraw ( props ) {
19
+ if ( ! this . canvas ) return ;
20
+
21
+ const context = this . canvas . getContext ( '2d' ) ;
22
+
23
+ this . canvas . width = props . image . width ;
24
+ this . canvas . height = props . image . height ;
25
+ context . drawImage ( props . image , 0 , 0 ) ;
26
+ const imageData = context . getImageData ( 0 , 0 , this . canvas . width , this . canvas . height ) ;
27
+ context . clearRect ( 0 , 0 , this . canvas . width , this . canvas . height ) ;
28
+
29
+ this . setState ( { isDrawing : true } ) ;
30
+
31
+ emojify ( this . state . mapping , props . scale , imageData ) . then ( ( emojifiedData ) => {
32
+ context . font = `${ props . scale * 1.3 } px sans-serif` ;
33
+ emojifiedData . forEach ( emojiData => {
34
+ context . fillText ( emojiData . emoji , emojiData . x , emojiData . y ) ;
35
+ } ) ;
36
+ this . setState ( { isDrawing : false } ) ;
37
+ } ) ;
38
+ }
39
+
40
+ /**
41
+ * @constructor EmojifyImageCustom
42
+ **/
43
+ constructor ( props ) {
44
+ super ( props ) ;
45
+
46
+ this . state = {
47
+ isMappingEmojis : true
48
+ } ;
49
+
50
+ this . _remap ( props . emojis ) ;
51
+ }
52
+
53
+ componentWillReceiveProps ( nextProps ) {
54
+ const shouldMapEmojis = nextProps . emojis !== this . props . emojis || nextProps . emojis . length !== this . props . emojis . length ;
55
+
56
+ if ( shouldMapEmojis ) {
57
+ this . setState ( { isMappingEmojis : true } ) ;
58
+ this . _remap ( nextProps . emojis ) ;
59
+ }
60
+ }
61
+
62
+ shouldComponentUpdate ( nextProps , nextState ) {
63
+ const shouldMapEmojis = nextProps . emojis !== this . props . emojis || nextProps . emojis . length !== this . props . emojis . length ;
64
+ const hasNewProps = nextProps . scale !== this . props . scale || nextProps . image !== this . props . image ;
65
+ const hasMappedEmojis = nextState . isMappingEmojis !== this . state . isMappingEmojis && nextState . isMappingEmojis === false ;
66
+
67
+ return shouldMapEmojis || hasNewProps || hasMappedEmojis ;
68
+ }
69
+
70
+ componentDidUpdate ( ) {
71
+ this . _redraw ( this . props ) ;
72
+ }
73
+
74
+ /**
75
+ * Render
76
+ * @returns {XML }
77
+ **/
78
+ render ( ) {
79
+ const Loader = this . props . loader ;
80
+ return (
81
+ < div style = { { display : 'flex' , flexFlow : 'column' } } >
82
+ { ( this . state . isDrawing || this . state . isMappingEmojis ) ? < Loader /> : null }
83
+ < canvas style = { { width : this . props . image . width , height : this . props . image . height } } ref = { r => ( this . canvas = r ) } />
84
+ </ div >
85
+ ) ;
86
+ }
87
+ }
88
+
89
+ EmojifyImageCustom . defaultProps = {
90
+ scale : 15 ,
91
+ loader : ( ) => < span > Loading...</ span >
92
+ } ;
93
+
94
+ EmojifyImageCustom . propTypes = {
95
+ /**
96
+ * An array of emojis to use when emojifying the image.
97
+ */
98
+ emojis : PropTypes . arrayOf ( PropTypes . string ) . isRequired ,
99
+ /**
100
+ * An ImageBitmap object of the actual image to use.
101
+ * The easiest way to get this object is to use `createImageBitmap()`.
102
+ * It gets an image source, which can be an <img>, SVG <image>, <video>, OffscreenCanvas, or <canvas> element, a Blob, ImageData, or another ImageBitmap object.
103
+ */
104
+ image : PropTypes . instanceOf ( ImageBitmap ) . isRequired ,
105
+ /**
106
+ * The number of (scale X scale) pixels to replace with one emoji.
107
+ */
108
+ scale : PropTypes . number ,
109
+ /**
110
+ * The loader component to render when either remapping colors to emojis or when emojifying the image
111
+ */
112
+ loader : PropTypes . func
113
+ } ;
0 commit comments