├── public
├── close.ico
├── favicon.ico
├── rotate2.png
├── manifest.json
└── index.html
├── src
├── imageModal
│ ├── images
│ │ ├── close.png
│ │ └── rotate.png
│ ├── common
│ │ ├── zoom.js
│ │ ├── onkeydown.js
│ │ ├── watermark.js
│ │ └── move.js
│ ├── index.css
│ └── index.js
├── modal_lib
│ ├── src
│ │ └── imageModal
│ │ │ ├── images
│ │ │ ├── close.png
│ │ │ └── rotate.png
│ │ │ ├── common
│ │ │ ├── zoom.js
│ │ │ ├── onkeydown.js
│ │ │ ├── move.js
│ │ │ └── watermark.js
│ │ │ ├── index.css
│ │ │ └── index.js
│ ├── package.json
│ ├── index.js
│ └── readme.md
├── index.css
├── App.js
├── index.js
├── logo.svg
├── Container.js
└── serviceWorker.js
├── .gitignore
├── config
├── jest
│ ├── cssTransform.js
│ └── fileTransform.js
├── paths.js
├── env.js
├── webpackDevServer.config.js
├── webpack.config.dev.js
└── webpack.config.prod.js
├── scripts
├── test.js
├── start.js
└── build.js
├── package.json
└── README.md
/public/close.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfjun/cxjReactImage/HEAD/public/close.ico
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfjun/cxjReactImage/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/public/rotate2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfjun/cxjReactImage/HEAD/public/rotate2.png
--------------------------------------------------------------------------------
/src/imageModal/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfjun/cxjReactImage/HEAD/src/imageModal/images/close.png
--------------------------------------------------------------------------------
/src/imageModal/images/rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfjun/cxjReactImage/HEAD/src/imageModal/images/rotate.png
--------------------------------------------------------------------------------
/src/modal_lib/src/imageModal/images/close.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfjun/cxjReactImage/HEAD/src/modal_lib/src/imageModal/images/close.png
--------------------------------------------------------------------------------
/src/modal_lib/src/imageModal/images/rotate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/wtfjun/cxjReactImage/HEAD/src/modal_lib/src/imageModal/images/rotate.png
--------------------------------------------------------------------------------
/src/modal_lib/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cxj-react-image",
3 | "version": "1.0.13",
4 | "description": "A simple image modal by react.",
5 | "main": "index.js",
6 | "scripts": {
7 | "test": "echo \"Error: no test specified\" && exit 1"
8 | },
9 | "author": "wtfjun",
10 | "license": "ISC"
11 | }
12 |
--------------------------------------------------------------------------------
/src/modal_lib/index.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 |
7 | var _imageModal = require('./src/imageModal');
8 |
9 | var _imageModal2 = _interopRequireDefault(_imageModal);
10 |
11 | function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12 |
13 | exports.default = _imageModal2.default;
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 |
--------------------------------------------------------------------------------
/public/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "short_name": "React App",
3 | "name": "Create React App Sample",
4 | "icons": [
5 | {
6 | "src": "favicon.ico",
7 | "sizes": "64x64 32x32 24x24 16x16",
8 | "type": "image/x-icon"
9 | }
10 | ],
11 | "start_url": ".",
12 | "display": "standalone",
13 | "theme_color": "#000000",
14 | "background_color": "#ffffff"
15 | }
16 |
--------------------------------------------------------------------------------
/config/jest/cssTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // This is a custom Jest transformer turning style imports into empty objects.
4 | // http://facebook.github.io/jest/docs/en/webpack.html
5 |
6 | module.exports = {
7 | process() {
8 | return 'module.exports = {};';
9 | },
10 | getCacheKey() {
11 | // The output is always the same.
12 | return 'cssTransform';
13 | },
14 | };
15 |
--------------------------------------------------------------------------------
/src/index.css:
--------------------------------------------------------------------------------
1 | .image-content {
2 | position: relative;
3 | overflow: hidden;
4 | }
5 |
6 | .image-item-menu:hover {
7 | opacity: 1;
8 | }
9 | .cxj-image-modal img {
10 | transition: all 0.3s;
11 | width: 100%;
12 | }
13 |
14 | .image-list {
15 | list-style-type: none;
16 | }
17 |
18 | .image-list li {
19 | float: left;
20 | margin-right: 10px;
21 | }
22 |
23 | .image-list li:hover {
24 | cursor: pointer;
25 | }
26 |
--------------------------------------------------------------------------------
/src/App.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Container from './Container';
3 |
4 | class App extends Component {
5 | render() {
6 | return (
7 |
14 | );
15 | }
16 | }
17 |
18 | export default App;
19 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import ReactDOM from 'react-dom';
3 | import './index.css';
4 | import App from './App';
5 | import * as serviceWorker from './serviceWorker';
6 |
7 | ReactDOM.render(, document.getElementById('root'));
8 |
9 | // If you want your app to work offline and load faster, you can change
10 | // unregister() to register() below. Note this comes with some pitfalls.
11 | // Learn more about service workers: http://bit.ly/CRA-PWA
12 | serviceWorker.unregister();
13 |
--------------------------------------------------------------------------------
/src/imageModal/common/zoom.js:
--------------------------------------------------------------------------------
1 | // 控制滚轮缩放
2 |
3 | const zoom = (onWheelEvent, dom) => {
4 | let e = onWheelEvent;
5 | let imageModalWidth = parseInt(dom.style.width);
6 | let modalLeft = parseInt(dom.style.left);
7 |
8 | // 计算缩放后的大小 每一次滚轮 100px
9 | let calcWidth = imageModalWidth - e.deltaY;
10 |
11 | // 限制最小 width = 400
12 | if (calcWidth <= 300) {
13 | return;
14 | }
15 |
16 | // 不让modal由于缩小消失在视野中
17 | if (modalLeft + calcWidth < 50) {
18 | return;
19 | }
20 |
21 | dom.style.width = `${calcWidth}px`;
22 | };
23 |
24 | export default zoom;
25 |
--------------------------------------------------------------------------------
/src/modal_lib/src/imageModal/common/zoom.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | // 控制滚轮缩放
7 |
8 | var zoom = function zoom(onWheelEvent, dom) {
9 | var e = onWheelEvent;
10 | var imageModalWidth = parseInt(dom.style.width);
11 | var modalLeft = parseInt(dom.style.left);
12 |
13 | // 计算缩放后的大小 每一次滚轮 100px
14 | var calcWidth = imageModalWidth - e.deltaY;
15 |
16 | // 限制最小 width = 400
17 | if (calcWidth <= 300) {
18 | return;
19 | }
20 |
21 | // 不让modal由于缩小消失在视野中
22 | if (modalLeft + calcWidth < 50) {
23 | return;
24 | }
25 |
26 | dom.style.width = calcWidth + "px";
27 | };
28 |
29 | exports.default = zoom;
--------------------------------------------------------------------------------
/src/imageModal/common/onkeydown.js:
--------------------------------------------------------------------------------
1 | const onkeydown = () => {
2 |
3 | // 全局设置 onkeyup ,避免多次监听 这里想不到更好的方案
4 | window.onkeyup = e => {
5 | let imageModalDoms = document.querySelectorAll('.image-modal');
6 | let imageModalMaxzIndex = localStorage.getItem('imageModalMaxzIndex');
7 | imageModalDoms.forEach(dom => {
8 | if (dom.style.zIndex == imageModalMaxzIndex) {
9 | let leftIconDom = dom.querySelector('#left-icon');
10 | let rightIconDom = dom.querySelector('#right-icon');
11 |
12 | // 键盘左键
13 | if (leftIconDom && e.keyCode == 37) {
14 | leftIconDom.click();
15 | }
16 |
17 | // 键盘右键
18 | if (rightIconDom && e.keyCode == 39) {
19 | rightIconDom.click();
20 | }
21 | }
22 | });
23 | };
24 | };
25 |
26 | export default onkeydown;
27 |
--------------------------------------------------------------------------------
/src/modal_lib/readme.md:
--------------------------------------------------------------------------------
1 | usege/用法:
2 |
3 | ```
4 | yarn add cxj-react-image
5 | // npm i cxj-react-image
6 | ```
7 |
8 | ```
9 | import ImageModal from 'cxj-react-image';
10 |
11 | this.next()} {/* 控制下一张 */}
14 | prev={() => this.prev()} {/* 控制上一张 */}
15 | closeModal={() => this.closeImg()} {/* 控制modal打开关闭 */}
16 | option={{
17 | move: true, {/* 控制拖动 */}
18 | waterMarkText: '多功能图片组件', {/* 设置水印文字 */}
19 | rotate: true, {/* 控制旋转 */}
20 | zoom: true {/* 控制放大缩小 */}
21 | }}
22 | />
23 | ```
24 |
25 | [github地址](https://github.com/wtfjun/cxjReactImage)
26 |
27 | [在线例子](https://wtfjun.github.io/cxjReactImage/build/)
28 |
29 | 交流请加wx: c13266836563
30 |
31 |
--------------------------------------------------------------------------------
/config/jest/fileTransform.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 |
5 | // This is a custom Jest transformer turning file imports into filenames.
6 | // http://facebook.github.io/jest/docs/en/webpack.html
7 |
8 | module.exports = {
9 | process(src, filename) {
10 | const assetFilename = JSON.stringify(path.basename(filename));
11 |
12 | if (filename.match(/\.svg$/)) {
13 | return `module.exports = {
14 | __esModule: true,
15 | default: ${assetFilename},
16 | ReactComponent: (props) => ({
17 | $$typeof: Symbol.for('react.element'),
18 | type: 'svg',
19 | ref: null,
20 | key: null,
21 | props: Object.assign({}, props, {
22 | children: ${assetFilename}
23 | })
24 | }),
25 | };`;
26 | }
27 |
28 | return `module.exports = ${assetFilename};`;
29 | },
30 | };
31 |
--------------------------------------------------------------------------------
/src/modal_lib/src/imageModal/common/onkeydown.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | var onkeydown = function onkeydown() {
7 |
8 | // 全局设置 onkeyup ,避免多次监听 这里想不到更好的方案
9 | window.onkeyup = function (e) {
10 | var imageModalDoms = document.querySelectorAll('.image-modal');
11 | var imageModalMaxzIndex = localStorage.getItem('imageModalMaxzIndex');
12 | imageModalDoms.forEach(function (dom) {
13 | if (dom.style.zIndex == imageModalMaxzIndex) {
14 | var leftIconDom = dom.querySelector('#left-icon');
15 | var rightIconDom = dom.querySelector('#right-icon');
16 |
17 | // 键盘左键
18 | if (leftIconDom && e.keyCode == 37) {
19 | leftIconDom.click();
20 | }
21 |
22 | // 键盘右键
23 | if (rightIconDom && e.keyCode == 39) {
24 | rightIconDom.click();
25 | }
26 | }
27 | });
28 | };
29 | };
30 |
31 | exports.default = onkeydown;
--------------------------------------------------------------------------------
/scripts/test.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | // Do this as the first thing so that any code reading it knows the right env.
4 | process.env.BABEL_ENV = 'test';
5 | process.env.NODE_ENV = 'test';
6 | process.env.PUBLIC_URL = '';
7 |
8 | // Makes the script crash on unhandled rejections instead of silently
9 | // ignoring them. In the future, promise rejections that are not handled will
10 | // terminate the Node.js process with a non-zero exit code.
11 | process.on('unhandledRejection', err => {
12 | throw err;
13 | });
14 |
15 | // Ensure environment variables are read.
16 | require('../config/env');
17 |
18 |
19 | const jest = require('jest');
20 | const execSync = require('child_process').execSync;
21 | let argv = process.argv.slice(2);
22 |
23 | function isInGitRepository() {
24 | try {
25 | execSync('git rev-parse --is-inside-work-tree', { stdio: 'ignore' });
26 | return true;
27 | } catch (e) {
28 | return false;
29 | }
30 | }
31 |
32 | function isInMercurialRepository() {
33 | try {
34 | execSync('hg --cwd . root', { stdio: 'ignore' });
35 | return true;
36 | } catch (e) {
37 | return false;
38 | }
39 | }
40 |
41 | // Watch unless on CI, in coverage mode, or explicitly running all tests
42 | if (
43 | !process.env.CI &&
44 | argv.indexOf('--coverage') === -1 &&
45 | argv.indexOf('--watchAll') === -1
46 | ) {
47 | // https://github.com/facebook/create-react-app/issues/5210
48 | const hasSourceControl = isInGitRepository() || isInMercurialRepository();
49 | argv.push(hasSourceControl ? '--watch' : '--watchAll');
50 | }
51 |
52 |
53 | jest.run(argv);
54 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
12 |
13 |
22 | React App
23 |
24 |
25 |
28 |
29 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/imageModal/common/watermark.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @overview: 水印组件
3 | */
4 |
5 | export default class WaterMark {
6 | constructor(container, option) {
7 | this.container = container;
8 | this.option = {
9 | width: '200px',
10 | height: '150px',
11 | opacity: .7,
12 | fillStyle: 'rgba(47, 205, 227, 0.3)',
13 | font: '20px microsoft yahei',
14 | textBaseline: 'middle',
15 | textAlign: 'center',
16 | fillText: '水印',
17 | ...option
18 | };
19 | }
20 |
21 | draw() {
22 | const {
23 | container,
24 | option: {
25 | width,
26 | height,
27 | opacity,
28 | fillStyle,
29 | font,
30 | textBaseline,
31 | textAlign,
32 | fillText,
33 | scrollHeight
34 | }
35 | } = this;
36 | const canvas = document.createElement('canvas');
37 | canvas.setAttribute('width', width);
38 | canvas.setAttribute('height', height);
39 | canvas.setAttribute('opacity', opacity);
40 | const ctx = canvas.getContext('2d');
41 |
42 | ctx.textAlign = textAlign;
43 | ctx.textBaseline = textBaseline;
44 | ctx.font = font;
45 | ctx.fillStyle = fillStyle;
46 | ctx.rotate(Math.PI / 180 * 30);
47 | ctx.fillText(fillText, 80, 10);
48 |
49 | var base64Url = canvas.toDataURL();
50 | const watermarkDiv = document.createElement('div');
51 | watermarkDiv.setAttribute('style', `
52 | position:absolute;
53 | top:0;
54 | left:0;
55 | width:100%;
56 | height:${scrollHeight || '100%'};
57 | z-index:1000;
58 | pointer-events:none;
59 | background-repeat:repeat;
60 | background-image:url('${base64Url}')`);
61 |
62 | if (typeof container === 'object') {
63 | container.style.position = 'relative';
64 | container.insertBefore(watermarkDiv, container.firstChild);
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/imageModal/common/move.js:
--------------------------------------------------------------------------------
1 | // 拖拽以及控制zIndex
2 | // dv: 移动的dom
3 | // ctDv: 控制dv的dom
4 | const move = (dv, ctDv) => {
5 | // 自己控制自己,且自己移动
6 | if (!ctDv) {
7 | ctDv = dv;
8 | }
9 |
10 | let imageModalMaxzIndex = localStorage.getItem('imageModalMaxzIndex');
11 | if (!imageModalMaxzIndex) {
12 | localStorage.setItem('imageModalMaxzIndex', 1000);
13 | dv.style.zIndex = 1000;
14 | } else {
15 | dv.style.zIndex = +imageModalMaxzIndex + 1;
16 | localStorage.setItem('imageModalMaxzIndex', dv.style.zIndex);
17 | }
18 |
19 | // 获取元素
20 | let x = 0;
21 | let y = 0;
22 | let l = 0;
23 | let t = 0;
24 | let isDown = false;
25 | const imageContentDom = dv.querySelector('.cxj-image-content');
26 | // 鼠标按下事件
27 | ctDv.onmousedown = function(e) {
28 |
29 | imageModalMaxzIndex = localStorage.getItem('imageModalMaxzIndex');
30 | if (dv.style.zIndex != imageModalMaxzIndex) {
31 | dv.style.zIndex = +imageModalMaxzIndex + 1;
32 | localStorage.setItem('imageModalMaxzIndex', dv.style.zIndex);
33 | }
34 |
35 | // e.preventDefault();
36 | // 获取x坐标和y坐标
37 | x = e.clientX;
38 | y = e.clientY;
39 |
40 | // 获取左部和顶部的偏移量
41 | l = dv.offsetLeft;
42 | t = dv.offsetTop;
43 | // 开关打开
44 | isDown = true;
45 | handleMove();
46 | };
47 | // 鼠标移动
48 | // 再包一层是为了方便注册 避免被替换
49 | function handleMove() {
50 | onmousemove = function(e) {
51 |
52 | if (isDown == false) {
53 | return;
54 | } else {
55 | // 鼠标点击在控制dom ctDv 的时候才取消默认事件、这样html才可复制
56 | e.preventDefault();
57 | }
58 |
59 | imageContentDom.style.opacity = .3;
60 | dv.style.border = '2px dashed #999';
61 | // 获取x和y
62 | let nx = e.clientX;
63 | let ny = e.clientY;
64 | // 计算移动后的左偏移量和顶部的偏移量
65 | let nl = nx - (x - l);
66 | let nt = ny - (y - t);
67 |
68 | dv.style.left = nl + 'px';
69 | dv.style.top = nt + 'px';
70 | };
71 | }
72 | // 鼠标抬起事件
73 | dv.onmouseup = function() {
74 | // 开关关闭
75 | isDown = false;
76 | // 取消透明
77 | imageContentDom.style.opacity = 1;
78 | dv.style.border = '2px solid transparent';
79 | };
80 | };
81 |
82 | export default move;
83 |
--------------------------------------------------------------------------------
/src/imageModal/index.css:
--------------------------------------------------------------------------------
1 | .cxj-image-modal {
2 | position: fixed;
3 | left: 30%;
4 | top: 40px;
5 | z-index: 1000;
6 | height: auto;
7 | transition: opacity .3s;
8 | border: 2px solid transparent;
9 | }
10 |
11 | #cxj-left-icon {
12 | z-index: 99999;
13 | position: absolute;
14 | left: 0;
15 | top: 50%;
16 | margin-top: -25px;
17 | display: block;
18 | height: 60px;
19 | line-height: 50px;
20 | font-size: 50px;
21 | cursor: pointer;
22 | color: #fff;
23 | background: #000;
24 | opacity: .3;
25 | transition: transform 0.3s;
26 | }
27 |
28 | #cxj-left-icon:hover {
29 | opacity: .5;
30 | }
31 | #cxj-right-icon {
32 | z-index: 99999;
33 | position: absolute;
34 | right: 0;
35 | top: 50%;
36 | margin-top: -25px;
37 | display: block;
38 | height: 60px;
39 | line-height: 50px;
40 | font-size: 50px;
41 | cursor: pointer;
42 | color: #fff;
43 | background: #000;
44 | opacity: .3;
45 | transition: transform 0.3s;
46 | }
47 | #cxj-right-icon:hover {
48 | opacity: .5;
49 | }
50 |
51 |
52 | .cxj-image-modal .image-item-menu {
53 | position: absolute;
54 | top: -44px;
55 | transition: all 0.3s;
56 | padding-top: 10px;
57 | width: 100%;
58 | text-align: center;
59 | font-size: 10px;
60 | }
61 |
62 | .cxj-image-modal .image-content {
63 | position: relative;
64 | overflow: hidden;
65 | }
66 |
67 | .cxj-image-modal img {
68 | transition: all 0.3s;
69 | width: 100%;
70 | }
71 |
72 | #cxj-close-icon {
73 | position: absolute;
74 | text-align: center;
75 | top: -6px;
76 | right: 12px;
77 | z-index: 1000;
78 | display: block;
79 | width: 17px;
80 | height: 50px;
81 | font-size: 30px;
82 | /* line-height: 50px; */
83 | /* background: #000; */
84 | /* opacity: .5; */
85 | }
86 |
87 | #cxj-close-icon:hover {
88 | cursor: pointer;
89 | }
90 |
91 | #cxj-rotate-right, #cxj-rotate-left {
92 | width: 19px;
93 | position: absolute;
94 | right: 44px;
95 | top: 8px;
96 | z-index: 1000;
97 | /* opacity: .5; */
98 | cursor: pointer;
99 | }
100 |
101 | #cxj-rotate-left {
102 | right: 76px;
103 | transform: rotate(180deg);
104 | top: 4px;
105 | }
106 |
107 | #cxj-rotate-right img, #cxj-rotate-left img {
108 | width: 100%;
109 | }
110 |
--------------------------------------------------------------------------------
/src/modal_lib/src/imageModal/common/move.js:
--------------------------------------------------------------------------------
1 |
2 |
3 | Object.defineProperty(exports, "__esModule", {
4 | value: true
5 | });
6 | // 拖拽以及控制zIndex
7 | // dv: 移动的dom
8 | // ctDv: 控制dv的dom
9 | var move = function move(dv, ctDv) {
10 | // 自己控制自己,且自己移动
11 | if (!ctDv) {
12 | ctDv = dv;
13 | }
14 |
15 | var imageModalMaxzIndex = localStorage.getItem('imageModalMaxzIndex');
16 | if (!imageModalMaxzIndex) {
17 | localStorage.setItem('imageModalMaxzIndex', 1000);
18 | dv.style.zIndex = 1000;
19 | } else {
20 | dv.style.zIndex = +imageModalMaxzIndex + 1;
21 | localStorage.setItem('imageModalMaxzIndex', dv.style.zIndex);
22 | }
23 |
24 | // 获取元素
25 | var x = 0;
26 | var y = 0;
27 | var l = 0;
28 | var t = 0;
29 | var isDown = false;
30 | var imageContentDom = dv.querySelector('.cxj-image-content');
31 | // 鼠标按下事件
32 | ctDv.onmousedown = function (e) {
33 |
34 | imageModalMaxzIndex = localStorage.getItem('imageModalMaxzIndex');
35 | if (dv.style.zIndex != imageModalMaxzIndex) {
36 | dv.style.zIndex = +imageModalMaxzIndex + 1;
37 | localStorage.setItem('imageModalMaxzIndex', dv.style.zIndex);
38 | }
39 |
40 | // e.preventDefault();
41 | // 获取x坐标和y坐标
42 | x = e.clientX;
43 | y = e.clientY;
44 |
45 | // 获取左部和顶部的偏移量
46 | l = dv.offsetLeft;
47 | t = dv.offsetTop;
48 | // 开关打开
49 | isDown = true;
50 | handleMove();
51 | };
52 | // 鼠标移动
53 | // 再包一层是为了方便注册 避免被替换
54 | function handleMove() {
55 | onmousemove = function onmousemove(e) {
56 |
57 | if (isDown == false) {
58 | return;
59 | } else {
60 | // 鼠标点击在控制dom ctDv 的时候才取消默认事件、这样html才可复制
61 | e.preventDefault();
62 | }
63 |
64 | imageContentDom.style.opacity = .3;
65 | dv.style.border = '2px dashed #999';
66 | // 获取x和y
67 | var nx = e.clientX;
68 | var ny = e.clientY;
69 | // 计算移动后的左偏移量和顶部的偏移量
70 | var nl = nx - (x - l);
71 | var nt = ny - (y - t);
72 |
73 | dv.style.left = nl + 'px';
74 | dv.style.top = nt + 'px';
75 | };
76 | }
77 | // 鼠标抬起事件
78 | dv.onmouseup = function () {
79 | // 开关关闭
80 | isDown = false;
81 | // 取消透明
82 | imageContentDom.style.opacity = 1;
83 | dv.style.border = '2px solid transparent';
84 | };
85 | };
86 |
87 | exports.default = move;
--------------------------------------------------------------------------------
/src/modal_lib/src/imageModal/index.css:
--------------------------------------------------------------------------------
1 | .cxj-image-modal {
2 | position: fixed;
3 | left: 30%;
4 | top: 40px;
5 | z-index: 1000;
6 | height: auto;
7 | transition: opacity .3s;
8 | border: 2px solid transparent;
9 | }
10 |
11 | #cxj-left-icon {
12 | z-index: 99999;
13 | position: absolute;
14 | left: 0;
15 | top: 50%;
16 | margin-top: -25px;
17 | display: block;
18 | height: 60px;
19 | line-height: 50px;
20 | font-size: 50px;
21 | cursor: pointer;
22 | color: #fff;
23 | background: #000;
24 | opacity: .3;
25 | transition: transform 0.3s;
26 | }
27 |
28 | #cxj-left-icon:hover {
29 | opacity: .5;
30 | }
31 | #cxj-right-icon {
32 | z-index: 99999;
33 | position: absolute;
34 | right: 0;
35 | top: 50%;
36 | margin-top: -25px;
37 | display: block;
38 | height: 60px;
39 | line-height: 50px;
40 | font-size: 50px;
41 | cursor: pointer;
42 | color: #fff;
43 | background: #000;
44 | opacity: .3;
45 | transition: transform 0.3s;
46 | }
47 | #cxj-right-icon:hover {
48 | opacity: .5;
49 | }
50 |
51 |
52 | .cxj-image-modal .image-item-menu {
53 | position: absolute;
54 | top: -44px;
55 | transition: all 0.3s;
56 | padding-top: 10px;
57 | width: 100%;
58 | text-align: center;
59 | font-size: 10px;
60 | }
61 |
62 | .cxj-image-modal .image-content {
63 | position: relative;
64 | overflow: hidden;
65 | }
66 |
67 | .cxj-image-modal img {
68 | transition: all 0.3s;
69 | width: 100%;
70 | }
71 |
72 | #cxj-close-icon {
73 | position: absolute;
74 | text-align: center;
75 | top: -6px;
76 | right: 12px;
77 | z-index: 1000;
78 | display: block;
79 | width: 17px;
80 | height: 50px;
81 | font-size: 30px;
82 | /* line-height: 50px; */
83 | /* background: #000; */
84 | /* opacity: .5; */
85 | }
86 |
87 | #cxj-close-icon:hover {
88 | cursor: pointer;
89 | }
90 |
91 | #cxj-rotate-right, #cxj-rotate-left {
92 | width: 19px;
93 | position: absolute;
94 | right: 44px;
95 | top: 8px;
96 | z-index: 1000;
97 | /* opacity: .5; */
98 | cursor: pointer;
99 | }
100 |
101 | #cxj-rotate-left {
102 | right: 76px;
103 | transform: rotate(180deg);
104 | top: 4px;
105 | }
106 |
107 | #cxj-rotate-right img, #cxj-rotate-left img {
108 | width: 100%;
109 | }
110 |
--------------------------------------------------------------------------------
/src/logo.svg:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/Container.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | // import ImageModal from 'cxj-react-image';
3 | import ImageModal from './imageModal';
4 |
5 | import './index.css';
6 |
7 | class Container extends Component {
8 | constructor(props) {
9 | super(props);
10 | this.state = {
11 | imageList: [],
12 | currentImageIndex: 0,
13 | modalVisible: false,
14 | };
15 | }
16 |
17 | componentDidMount() {
18 | // 可以在这里异步获取图片数组
19 | this.setState({
20 | imageList: [
21 | 'http://pic39.photophoto.cn/20160630/1155115644653376_b.jpg',
22 | 'http://image.biaobaiju.com/uploads/20180801/23/1533136618-ARPmGQwjvD.jpg',
23 | 'http://img18.3lian.com/d/file/201712/08/8c2b94d5ec0bf634e3881b4ad5568b57.png',
24 | 'http://img3.duitang.com/uploads/blog/201509/15/20150915125228_ixEts.jpeg',
25 | 'https://s10.sinaimg.cn/mw690/001whaJHzy7kChbjZRn59&690',
26 | 'https://upload-images.jianshu.io/upload_images/5691297-648aed056c02698f.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/542/format/webp'
27 | ]
28 | })
29 | }
30 |
31 | prev() {
32 | const { currentImageIndex } = this.state;
33 | if (currentImageIndex > 0) {
34 | this.setState({ currentImageIndex: currentImageIndex - 1 });
35 | }
36 | }
37 |
38 | next() {
39 | const { currentImageIndex, imageList } = this.state;
40 | if (currentImageIndex < imageList.length - 1) {
41 | this.setState({ currentImageIndex: currentImageIndex + 1 });
42 | }
43 | }
44 |
45 | handleImgClick = index => {
46 | this.setState({
47 | modalVisible: true,
48 | currentImageIndex: index
49 | });
50 |
51 | }
52 |
53 | closeImg = () => this.setState({ modalVisible: false });
54 |
55 | render() {
56 | const { modalVisible, imageList, currentImageIndex } = this.state;
57 | return (
58 |
59 |
60 | {imageList.map((src, i) => -
61 |
this.handleImgClick(i) }
67 | />
68 | )}
69 |
70 |
71 |
72 | {modalVisible &&
this.next()}
75 | prev={() => this.prev()}
76 | closeModal={() => this.closeImg()}
77 | option={{
78 | move: true,
79 | waterMarkText: '多功能图片组件',
80 | rotate: true,
81 | zoom: true
82 | }}
83 | />}
84 |
85 | );
86 | }
87 | }
88 |
89 | export default Container;
90 |
--------------------------------------------------------------------------------
/src/imageModal/index.js:
--------------------------------------------------------------------------------
1 | import React, { Component } from 'react';
2 | import Move from './common/move';
3 | import Zoom from './common/zoom';
4 | import WaterMark from './common/watermark';
5 |
6 | import RotateIcon from './images/rotate.png';
7 | import CloseIcon from './images/close.png';
8 |
9 | import './index.css';
10 | class ImageModal extends Component {
11 | constructor(props) {
12 | super(props);
13 | this.state = {
14 | imgRotate: 0
15 | }
16 | }
17 |
18 | componentDidMount() {
19 | const {
20 | option: {
21 | move, waterMarkText
22 | }
23 | } = this.props;
24 | let imageModalDom = this.refs.imageModal;
25 | let imageContentDom = imageModalDom.querySelector('.cxj-image-content');
26 |
27 | // 注册拖拽
28 | move && Move(imageModalDom);
29 |
30 | // 画水印 部分图片不画水印,比如聊天图片
31 | if (waterMarkText) {
32 | new WaterMark(imageContentDom, { fillText: waterMarkText }).draw();
33 | }
34 | }
35 |
36 | componentWillReceiveProps(nextProps) {
37 | const {
38 | src
39 | } = this.props;
40 | if (src !== nextProps.src) {
41 | this.setState({ imgRotate: 0 });
42 | }
43 | }
44 |
45 | handleRotateRight = () => {
46 | this.setState({ imgRotate: this.state.imgRotate + 90 });
47 | }
48 |
49 | handleRotateLeft = () => {
50 | this.setState({ imgRotate: this.state.imgRotate - 90 })
51 | }
52 |
53 | render() {
54 | let { src, prev, next, closeModal, option: { rotate, zoom } } = this.props;
55 | let { imgRotate } = this.state;
56 | return (
57 |
58 |
zoom && Zoom(e, this.refs.imageModal)}
63 | onContextMenu={e => {
64 | e.preventDefault();
65 | +e.button === 2 && closeModal();}
66 | }
67 | >
68 | {rotate &&
69 |
70 | }
71 | {rotate &&
72 |
73 | }
74 |
75 |
76 |
77 | {prev &&
<}
78 | {next &&
>}
79 |
80 |

81 |
82 |
83 |
84 | );
85 | }
86 | }
87 |
88 | export default ImageModal;
89 |
--------------------------------------------------------------------------------
/config/paths.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | const path = require('path');
4 | const fs = require('fs');
5 | const url = require('url');
6 |
7 | // Make sure any symlinks in the project folder are resolved:
8 | // https://github.com/facebook/create-react-app/issues/637
9 | const appDirectory = fs.realpathSync(process.cwd());
10 | const resolveApp = relativePath => path.resolve(appDirectory, relativePath);
11 |
12 | const envPublicUrl = process.env.PUBLIC_URL;
13 |
14 | function ensureSlash(inputPath, needsSlash) {
15 | const hasSlash = inputPath.endsWith('/');
16 | if (hasSlash && !needsSlash) {
17 | return inputPath.substr(0, inputPath.length - 1);
18 | } else if (!hasSlash && needsSlash) {
19 | return `${inputPath}/`;
20 | } else {
21 | return inputPath;
22 | }
23 | }
24 |
25 | const getPublicUrl = appPackageJson =>
26 | envPublicUrl || require(appPackageJson).homepage;
27 |
28 | // We use `PUBLIC_URL` environment variable or "homepage" field to infer
29 | // "public path" at which the app is served.
30 | // Webpack needs to know it to put the right