image-beautifier 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/.eslintrc.cjs +25 -0
  2. package/LICENSE +21 -0
  3. package/README.md +28 -0
  4. package/favicon.svg +20 -0
  5. package/index.html +13 -0
  6. package/jsconfig.json +12 -0
  7. package/package.json +53 -0
  8. package/postcss.config.mjs +6 -0
  9. package/preview.png +0 -0
  10. package/public/vite.svg +1 -0
  11. package/src/App.jsx +29 -0
  12. package/src/assets/blur.svg +8 -0
  13. package/src/assets/color.svg +1 -0
  14. package/src/assets/demo.png +0 -0
  15. package/src/assets/pencil.png +0 -0
  16. package/src/assets/rotate.png +0 -0
  17. package/src/components/ColorPicker.jsx +27 -0
  18. package/src/components/Icon.jsx +81 -0
  19. package/src/components/editor/Editor.jsx +19 -0
  20. package/src/components/editor/HotKeys.jsx +48 -0
  21. package/src/components/editor/View.jsx +167 -0
  22. package/src/components/editor/Zoom.jsx +54 -0
  23. package/src/components/editor/layers/FrameBox.jsx +51 -0
  24. package/src/components/editor/layers/Screenshot.jsx +89 -0
  25. package/src/components/editor/layers/ShapeLine.jsx +97 -0
  26. package/src/components/editor/layers/Watermark.jsx +41 -0
  27. package/src/components/header/EmojiSelect.jsx +35 -0
  28. package/src/components/header/Header.jsx +134 -0
  29. package/src/components/header/Logo.jsx +29 -0
  30. package/src/components/header/MediaLogo.jsx +10 -0
  31. package/src/components/header/WidthDropdown.jsx +74 -0
  32. package/src/components/init/Init.jsx +77 -0
  33. package/src/components/sideBar/BackgroundSelect.jsx +49 -0
  34. package/src/components/sideBar/CropperImage.jsx +73 -0
  35. package/src/components/sideBar/CustomSize.jsx +60 -0
  36. package/src/components/sideBar/DownloadBar.jsx +179 -0
  37. package/src/components/sideBar/DrawerBar.jsx +64 -0
  38. package/src/components/sideBar/Position.jsx +45 -0
  39. package/src/components/sideBar/SideBar.jsx +131 -0
  40. package/src/components/sideBar/SizeBar.jsx +114 -0
  41. package/src/components/sideBar/Watermark.jsx +59 -0
  42. package/src/hooks/useKeyboardShortcuts.js +28 -0
  43. package/src/hooks/usePaste.js +21 -0
  44. package/src/index.js +1 -0
  45. package/src/main.jsx +9 -0
  46. package/src/stores/editor.js +106 -0
  47. package/src/stores/index.js +7 -0
  48. package/src/stores/option.js +96 -0
  49. package/src/style/main.css +132 -0
  50. package/src/utils/UndoRedoManager.js +83 -0
  51. package/src/utils/backgroundConfig.js +387 -0
  52. package/src/utils/captureScreen.js +28 -0
  53. package/src/utils/sizeConfig.js +163 -0
  54. package/src/utils/utils.js +154 -0
  55. package/tailwind.config.mjs +15 -0
  56. package/vite.config.js +21 -0
@@ -0,0 +1,154 @@
1
+ import { customAlphabet } from 'nanoid/non-secure';
2
+ import { clsx } from 'clsx';
3
+ import { twMerge } from 'tailwind-merge';
4
+
5
+ export const isAppleDevice = () => {
6
+ const PLATFORM = typeof navigator === 'object' ? navigator.platform : '';
7
+ return /Mac|iPod|iPhone|iPad/.test(PLATFORM);
8
+ };
9
+
10
+ export const modKey = (isAppleDevice() ? '⌘' : 'Ctrl');
11
+
12
+ export const supportImg = [
13
+ 'image/jpeg',
14
+ 'image/png',
15
+ 'image/bmp',
16
+ 'image/gif',
17
+ 'image/webp',
18
+ ];
19
+
20
+ export function cn(...inputs) {
21
+ return twMerge(clsx(inputs));
22
+ }
23
+
24
+ // 7-character random string
25
+ export const nanoid = customAlphabet(
26
+ '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz',
27
+ 7
28
+ );
29
+
30
+ export const toDownloadFile = (url, name) => {
31
+ let tmpLink = document.createElement('a');
32
+ tmpLink.href = url;
33
+ tmpLink.download = name;
34
+ tmpLink.style = 'position: absolute; z-index: -111; visibility: none;';
35
+ document.body.appendChild(tmpLink);
36
+ tmpLink.click();
37
+ document.body.removeChild(tmpLink);
38
+ tmpLink = null;
39
+ };
40
+
41
+ export const computedSize = (w, h, maxWidth = 950, maxHeight = 450) => {
42
+ let width = w;
43
+ let height = h;
44
+
45
+ // 检查图片是否超过最大宽度
46
+ if (width > maxWidth) {
47
+ height *= maxWidth / width;
48
+ width = maxWidth;
49
+ }
50
+
51
+ // 检查图片是否超过最大高度
52
+ if (height > maxHeight) {
53
+ width *= maxHeight / height;
54
+ height = maxHeight;
55
+ }
56
+ return { width, height };
57
+ };
58
+
59
+ export const getImage = (src) => {
60
+ const img = new Image();
61
+ // cors
62
+ if (!src.startsWith('data')) {
63
+ img.crossOrigin = 'Anonymous';
64
+ }
65
+ return new Promise(function (resolve, reject) {
66
+ img.onload = function () {
67
+ resolve(img);
68
+ };
69
+ const errorHandler = function () {
70
+ return reject(
71
+ new Error('An error occurred attempting to load image')
72
+ );
73
+ };
74
+ img.onerror = errorHandler;
75
+ img.onabort = errorHandler;
76
+ img.src = src;
77
+ });
78
+ };
79
+
80
+ // type: ['top-left', 'top', 'top-right', 'left', 'center', 'right', 'bottom-left', 'bottom', 'bottom-right']
81
+ export const getPosition = (type, xw, xh) => {
82
+ if (type === 'top-left') return {
83
+ x: 0,
84
+ y: 0
85
+ }
86
+ if (type === 'top') return {
87
+ x: xw / 2,
88
+ y: 0
89
+ }
90
+ if (type === 'top-right') return {
91
+ x: xw,
92
+ y: 0
93
+ }
94
+ if (type === 'left') return {
95
+ x: 0,
96
+ y: xh /2
97
+ }
98
+ if (type === 'right') return {
99
+ x: xw,
100
+ y: xh / 2
101
+ }
102
+ if (type === 'bottom-left') return {
103
+ x: 0,
104
+ y: xh
105
+ }
106
+ if (type === 'bottom') return {
107
+ x: xw / 2,
108
+ y: xh
109
+ }
110
+ if (type === 'bottom-right') return {
111
+ x: xw,
112
+ y: xh
113
+ }
114
+ return { x: xw / 2, y: xh / 2 };
115
+ }
116
+
117
+ export const calculateRotatedRectDimensions = (width, height, angleDegrees) => {
118
+ const angleRadians = angleDegrees * (Math.PI / 180);
119
+ const newWidth = Math.abs(width * Math.cos(angleRadians)) + Math.abs(height * Math.sin(angleRadians));
120
+ const newHeight = Math.abs(width * Math.sin(angleRadians)) + Math.abs(height * Math.cos(angleRadians));
121
+
122
+ return {
123
+ width: Math.round(newWidth),
124
+ height: Math.round(newHeight)
125
+ };
126
+ };
127
+
128
+ export const text2Svg = ({ text, color, angleDegrees }) => {
129
+ const div = document.createElement('div');
130
+ div.style = `text-align:center;white-space:nowrap;line-height:100px;transform: rotate(${angleDegrees}deg);position: absolute;top:0;left:0;opacity: 0;`;
131
+ const span = document.createElement('span');
132
+ span.style.color = color;
133
+ span.style.fontSize = '36px';
134
+ span.innerText = text;
135
+ div.append(span);
136
+ document.body.append(div);
137
+ const { width, height } = div.getBoundingClientRect();
138
+ document.body.removeChild(div);
139
+ const result = calculateRotatedRectDimensions(width, height, angleDegrees);
140
+ const divHtml = `
141
+ <div xmlns="http://www.w3.org/1999/xhtml" style="text-align:center;white-space:nowrap;line-height:${result.height}px;transform:rotate(${angleDegrees}deg);">
142
+ <span style="color:${color};font-size:36px;">${text}</span>
143
+ </div>
144
+ `;
145
+ const data = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${result.width} ${result.height}" width="${result.width}" height="${result.height}">
146
+ <foreignObject width="100%" height="100%">
147
+ ${divHtml}
148
+ </foreignObject>
149
+ </svg>`;
150
+ const svgFile = new Blob([data], { type: 'image/svg+xml;charset=utf-8' });
151
+ const DOMURL = window.URL || window.webkitURL || window;
152
+ const url = DOMURL.createObjectURL(svgFile);
153
+ return url;
154
+ };
@@ -0,0 +1,15 @@
1
+ /** @type {import('tailwindcss').Config} */
2
+ export default {
3
+ content: ['./src/**/*.{html,js,jsx}'],
4
+ theme: {
5
+ container: {
6
+ center: true,
7
+ padding: '2rem',
8
+ screens: {
9
+ '2xl': '1400px',
10
+ },
11
+ },
12
+ extend: {},
13
+ },
14
+ plugins: [],
15
+ };
package/vite.config.js ADDED
@@ -0,0 +1,21 @@
1
+ import { defineConfig } from 'vite'
2
+ import path from 'path';
3
+ import react from '@vitejs/plugin-react-swc'
4
+
5
+ const resolve = (url) => path.resolve(__dirname, url);
6
+
7
+ // https://vitejs.dev/config/
8
+ export default defineConfig({
9
+ resolve: {
10
+ alias: {
11
+ '@components': resolve('./src/components'),
12
+ '@assets': resolve('./src/assets'),
13
+ '@style': resolve('./src/style'),
14
+ '@stores': resolve('./src/stores/index.js'),
15
+ '@utils': resolve('./src/utils'),
16
+ '@hooks': resolve('./src/hooks'),
17
+ },
18
+ extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json"], // 省略扩展名
19
+ },
20
+ plugins: [react()],
21
+ })