├── .gitattributes
├── .gitignore
├── README.md
├── example
├── App.js
├── img
│ ├── beach-large.jpg
│ ├── beach-small.jpg
│ ├── cat-large.jpg
│ ├── cat-small.jpg
│ ├── fall-large.jpg
│ └── fall-small.jpg
└── index.html
├── index.js
├── package.json
├── src
├── App.js
└── ImageMagnifier.js
└── webpack.config.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 |
5 | # Runtime data
6 | pids
7 | *.pid
8 | *.seed
9 |
10 | # Directory for instrumented libs generated by jscoverage/JSCover
11 | lib-cov
12 |
13 | # Coverage directory used by tools like istanbul
14 | coverage
15 |
16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
17 | .grunt
18 |
19 | # Compiled binary addons (http://nodejs.org/api/addons.html)
20 | build/Release
21 |
22 | # Dependency directory
23 | # Commenting this out is preferred by some people, see
24 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
25 | node_modules
26 |
27 | # Users Environment Variables
28 | .lock-wscript
29 |
30 | # =========================
31 | # Operating System Files
32 | # =========================
33 |
34 | # OSX
35 | # =========================
36 |
37 | .DS_Store
38 | .AppleDouble
39 | .LSOverride
40 |
41 | # Thumbnails
42 | ._*
43 |
44 | # Files that might appear on external disk
45 | .Spotlight-V100
46 | .Trashes
47 |
48 | # Directories potentially created on remote AFP share
49 | .AppleDB
50 | .AppleDesktop
51 | Network Trash Folder
52 | Temporary Items
53 | .apdisk
54 |
55 | # Windows
56 | # =========================
57 |
58 | # Windows image file caches
59 | Thumbs.db
60 | ehthumbs.db
61 |
62 | # Folder config file
63 | Desktop.ini
64 |
65 | # Recycle Bin used on file shares
66 | $RECYCLE.BIN/
67 |
68 | # Windows Installer files
69 | *.cab
70 | *.msi
71 | *.msm
72 | *.msp
73 |
74 | # Windows shortcuts
75 | *.lnk
76 |
77 | # Webstorm
78 |
79 | .idea
80 | .idea/vcs.xml
81 | .idea/modules.xml
82 | .idea/react-image-magnifier.iml
83 | .idea/scopes/scope_settings.xml
84 | .idea/vcs.xml
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # react-image-magnifier
2 | A react component that accepts a high-res source image and produces a magnifier window on mouse hover over the part of the image the cursor is over
3 |
4 |
5 | ## Demo
6 |
7 | 
8 |
9 |
10 | ## Usage
11 |
12 | ```bash
13 | > npm install --save react-image-magnifier
14 | ```
15 |
16 | ```jsx
17 | var ImageMagnifer = require('react-image-magnifier');
18 |
19 | var App = React.createClass({
20 |
21 | render () {
22 | return (
23 |
36 | );
37 | }
38 | });
39 | ```
40 |
41 | ## API (props)
42 |
43 | | Prop | Required | Default | Type | Description |
44 | | :------------ |:---:|:---------------:| :---------------| :-----|
45 | | `image` | YES | | `{ src, width, height }` | the src, size of the non-zoomed-in image |
46 | | `zoomImage` | YES | | `{ src, width, height }` | the src, size of the zoomed-in image |
47 | | `cursorOffset` | NO | `{ x: 0, y: 0 }` | `{ x, y }` | the offset of the zoom bubble from the cursor |
48 | | `size` | NO | `200` | `Number` | the size of the magnifier window |
--------------------------------------------------------------------------------
/example/img/beach-large.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelandrichardson/react-image-magnifier/1b1b977e9666b3b2b90017202b9f7b21cbb5a02d/example/img/beach-large.jpg
--------------------------------------------------------------------------------
/example/img/beach-small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelandrichardson/react-image-magnifier/1b1b977e9666b3b2b90017202b9f7b21cbb5a02d/example/img/beach-small.jpg
--------------------------------------------------------------------------------
/example/img/cat-large.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelandrichardson/react-image-magnifier/1b1b977e9666b3b2b90017202b9f7b21cbb5a02d/example/img/cat-large.jpg
--------------------------------------------------------------------------------
/example/img/cat-small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelandrichardson/react-image-magnifier/1b1b977e9666b3b2b90017202b9f7b21cbb5a02d/example/img/cat-small.jpg
--------------------------------------------------------------------------------
/example/img/fall-large.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelandrichardson/react-image-magnifier/1b1b977e9666b3b2b90017202b9f7b21cbb5a02d/example/img/fall-large.jpg
--------------------------------------------------------------------------------
/example/img/fall-small.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/lelandrichardson/react-image-magnifier/1b1b977e9666b3b2b90017202b9f7b21cbb5a02d/example/img/fall-small.jpg
--------------------------------------------------------------------------------
/example/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | React Image Magnifier
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./src/ImageMagnifier');
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-image-magnifier",
3 | "version": "1.0.0",
4 | "description": "A react component that accepts a high-res source image and produces a magnifier window on mouse hover over the part of the image the cursor is over",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "repository": {
10 | "type": "git",
11 | "url": "https://github.com/lelandrichardson/react-image-magnifier.git"
12 | },
13 | "keywords": [
14 | "react",
15 | "react-component",
16 | "image",
17 | "magnifier"
18 | ],
19 | "author": "Leland Richardson",
20 | "license": "MIT",
21 | "dependencies": {
22 | "react": "^0.13.3"
23 | },
24 | "devDependencies": {
25 | "es5-shim": "^4.1.3",
26 | "node-libs-browser": "^0.5.2",
27 | "webpack": "^1.9.10",
28 | "babel": "^5.4.7",
29 | "babel-core": "^5.4.7",
30 | "babel-loader": "^5.1.3",
31 | "babel-runtime": "^5.4.7"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | // polyfills
2 | require('es5-shim');
3 | require('es5-shim/es5-sham');
4 |
5 | var React = require('react');
6 | var ImageMagnifier = require('./ImageMagnifier');
7 |
8 | var App = React.createClass({
9 |
10 | render () {
11 | return (
12 |
13 |
25 |
38 |
50 |
51 | );
52 | }
53 | });
54 |
55 | React.render(, document.getElementById("mount"));
--------------------------------------------------------------------------------
/src/ImageMagnifier.js:
--------------------------------------------------------------------------------
1 | var React = require('react');
2 |
3 | var Magnifier = React.createClass({
4 |
5 | propTypes: {
6 |
7 | // the size of the magnifier window
8 | size: React.PropTypes.number.isRequired,
9 |
10 | // x position on screen
11 | x: React.PropTypes.number.isRequired,
12 |
13 | // y position on screen
14 | y: React.PropTypes.number.isRequired,
15 |
16 | // x position relative to the image
17 | offsetX: React.PropTypes.number.isRequired,
18 |
19 | // y position relative to the image
20 | offsetY: React.PropTypes.number.isRequired,
21 |
22 | // the offset of the zoom bubble from the cursor
23 | cursorOffset: React.PropTypes.shape({
24 | x: React.PropTypes.number.isRequired,
25 | y: React.PropTypes.number.isRequired
26 | }).isRequired,
27 |
28 | // the size of the non-zoomed-in image
29 | smallImage: React.PropTypes.shape({
30 | src: React.PropTypes.string.isRequired,
31 | width: React.PropTypes.number.isRequired,
32 | height: React.PropTypes.number.isRequired
33 | }).isRequired,
34 |
35 | // the size of the zoomed-in image
36 | zoomImage: React.PropTypes.shape({
37 | src: React.PropTypes.string.isRequired,
38 | width: React.PropTypes.number.isRequired,
39 | height: React.PropTypes.number.isRequired
40 | }).isRequired
41 | },
42 |
43 | render () {
44 | var props = this.props;
45 | var halfSize = props.size / 2;
46 | var magX = props.zoomImage.width / props.smallImage.width;
47 | var magY = props.zoomImage.height / props.smallImage.height;
48 | var bgX = -(props.offsetX*magX - halfSize);
49 | var bgY = -(props.offsetY*magY - halfSize);
50 | var isVisible = props.offsetY < props.smallImage.height &&
51 | props.offsetX < props.smallImage.width &&
52 | props.offsetY > 0 &&
53 | props.offsetX > 0;
54 | return (
55 |
77 | );
78 | }
79 | });
80 |
81 | function getOffset(el) {
82 | var x = 0;
83 | var y = 0;
84 | while( el && !isNaN( el.offsetLeft ) && !isNaN( el.offsetTop ) ) {
85 | x += el.offsetLeft - el.scrollLeft;
86 | y += el.offsetTop - el.scrollTop;
87 | el = el.offsetParent;
88 | }
89 | return { x, y };
90 | }
91 |
92 | var ImageMagnifier = React.createClass({
93 |
94 | propTypes: {
95 |
96 | // the size of the magnifier window
97 | size: React.PropTypes.number,
98 |
99 | // the offset of the zoom bubble from the cursor
100 | cursorOffset: React.PropTypes.shape({
101 | x: React.PropTypes.number.isRequired,
102 | y: React.PropTypes.number.isRequired
103 | }),
104 |
105 | // the size of the non-zoomed-in image
106 | image: React.PropTypes.shape({
107 | src: React.PropTypes.string.isRequired,
108 | width: React.PropTypes.number.isRequired,
109 | height: React.PropTypes.number.isRequired
110 | }).isRequired,
111 |
112 | // the size of the zoomed-in image
113 | zoomImage: React.PropTypes.shape({
114 | src: React.PropTypes.string.isRequired,
115 | width: React.PropTypes.number.isRequired,
116 | height: React.PropTypes.number.isRequired
117 | }).isRequired
118 | },
119 |
120 | portalElement: null,
121 |
122 | getDefaultProps () {
123 | return {
124 | size: 200,
125 | cursorOffset: { x: 0, y: 0 }
126 | };
127 | },
128 |
129 | getInitialState () {
130 | return {
131 | x: 0,
132 | y: 0,
133 | offsetX: -1,
134 | offsetY: -1
135 | };
136 | },
137 |
138 | componentDidMount() {
139 | document.addEventListener('mousemove', this.onMouseMove);
140 | if (!this.portalElement) {
141 | this.portalElement = document.createElement('div');
142 | document.body.appendChild(this.portalElement);
143 | }
144 | this.componentDidUpdate();
145 | },
146 |
147 | componentWillUnmount() {
148 | document.removeEventListener('mousemove', this.onMouseMove);
149 | document.body.removeChild(this.portalElement);
150 | this.portalElement = null;
151 | },
152 |
153 | onMouseMove (e) {
154 | var offset = getOffset(this.getDOMNode());
155 |
156 | this.setState({
157 | x: e.x + window.scrollX,
158 | y: e.y + window.scrollY,
159 | offsetX: e.x - offset.x,
160 | offsetY: e.y - offset.y
161 | });
162 | },
163 |
164 | componentDidUpdate() {
165 | React.render(, this.portalElement);
172 | },
173 |
174 | render () {
175 | return (
176 |
177 | );
178 | }
179 | });
180 |
181 | module.exports = ImageMagnifier;
--------------------------------------------------------------------------------
/webpack.config.js:
--------------------------------------------------------------------------------
1 | var path = require('path');
2 | var webpack = require('webpack');
3 |
4 | module.exports = {
5 | cache: true,
6 | entry: {
7 | App: './src/App'
8 | },
9 | output: {
10 | path: path.join(__dirname, 'example'),
11 | publicPath: 'example',
12 | filename: '[name].js'
13 | },
14 | module: {
15 | loaders: [
16 | {
17 | test: /\.jsx?$/,
18 | exclude: /node_modules/,
19 | loader: 'babel',
20 | query: {
21 | optional: [
22 | 'runtime',
23 | 'minification.propertyLiterals'
24 | ]
25 | }
26 | }
27 | ],
28 | noParse: /\.min\.js/
29 | },
30 | resolve: {
31 | modulesDirectories: ['src/Components', 'src/Views', 'src/Styles', 'node_modules'],
32 | extensions: ['', '.js', '.jsx', '.json']
33 | },
34 | plugins: [
35 | new webpack.NoErrorsPlugin()
36 | ]
37 | };
--------------------------------------------------------------------------------