Showing
7 changed files
with
289 additions
and
35 deletions
| ... | ... | @@ -8,6 +8,7 @@ |
| 8 | 8 | |
| 9 | 9 | <body> |
| 10 | 10 | <link rel="stylesheet" href="css/main.css" /> |
| 11 | + <link rel="stylesheet" href="./js/libs/spin/spin.css"> | |
| 11 | 12 | <link rel="stylesheet" href="js/libs/codemirror/codemirror.css" /> |
| 12 | 13 | <link rel="stylesheet" href="js/libs/codemirror/theme/monokai.css" /> |
| 13 | 14 | <link rel="stylesheet" href="js/libs/codemirror/addon/dialog.css" /> | ... | ... |
| ... | ... | @@ -5,6 +5,7 @@ import html2canvas from 'html2canvas' |
| 5 | 5 | import { fetchRouteParamsLocation } from '@/utils' |
| 6 | 6 | import { uploadFile } from '@/api/external/contentSave/content' |
| 7 | 7 | import { base64toFile } from '../utils/Base64ToFile.js' |
| 8 | +import { useSpin } from '../js/libs/spin/useSpin.js' | |
| 8 | 9 | |
| 9 | 10 | function MenubarFile(editor) { |
| 10 | 11 | const strings = editor.strings |
| ... | ... | @@ -72,7 +73,7 @@ function MenubarFile(editor) { |
| 72 | 73 | const loader = new THREE.FileLoader() |
| 73 | 74 | |
| 74 | 75 | for (let i = 0; i < examples.length; i++) { |
| 75 | - ;(function (i) { | |
| 76 | + ; (function (i) { | |
| 76 | 77 | const example = examples[i] |
| 77 | 78 | |
| 78 | 79 | const option = new UIRow() |
| ... | ... | @@ -148,31 +149,38 @@ function MenubarFile(editor) { |
| 148 | 149 | .onClick(async function () { |
| 149 | 150 | // 获取缩略图片 |
| 150 | 151 | const range = document.querySelector('#viewport').children[3] |
| 151 | - // 生成图片 | |
| 152 | - const canvasImage = await html2canvas(range, { | |
| 153 | - backgroundColor: null, | |
| 154 | - allowTaint: true, | |
| 155 | - useCORS: true, | |
| 156 | - logging: false | |
| 157 | - }) | |
| 158 | - // 上传预览图 | |
| 159 | - const uploadParams = new FormData() | |
| 160 | - uploadParams.append( | |
| 161 | - 'file', | |
| 162 | - base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`) | |
| 163 | - ) | |
| 164 | - const uploadRes = await uploadFile(uploadParams) | |
| 165 | - const file_json = editor.toJSON() | |
| 166 | - const paramsStr = window.location.search | |
| 167 | - const params = new URLSearchParams(paramsStr) | |
| 168 | - const file_uuid = params.get('three_file_uuid') | |
| 169 | - await saveOrUpdateThreeJsModel({ | |
| 170 | - id: file_uuid, | |
| 171 | - imageUrl: uploadRes?.fileDownloadUri, | |
| 172 | - data: file_json | |
| 173 | - }) | |
| 174 | - const { success } = useMessage() | |
| 175 | - success('保存成功') | |
| 152 | + const { spin, stop } = useSpin() | |
| 153 | + try { | |
| 154 | + spin() | |
| 155 | + // 生成图片 | |
| 156 | + const canvasImage = await html2canvas(range, { | |
| 157 | + backgroundColor: null, | |
| 158 | + allowTaint: true, | |
| 159 | + useCORS: true, | |
| 160 | + logging: false | |
| 161 | + }) | |
| 162 | + // 上传预览图 | |
| 163 | + const uploadParams = new FormData() | |
| 164 | + uploadParams.append( | |
| 165 | + 'file', | |
| 166 | + base64toFile(canvasImage.toDataURL(), `${fetchRouteParamsLocation()}_index_preview.png`) | |
| 167 | + ) | |
| 168 | + const uploadRes = await uploadFile(uploadParams) | |
| 169 | + const file_json = editor.toJSON() | |
| 170 | + const paramsStr = window.location.search | |
| 171 | + const params = new URLSearchParams(paramsStr) | |
| 172 | + const file_uuid = params.get('three_file_uuid') | |
| 173 | + console.log(file_json) | |
| 174 | + await saveOrUpdateThreeJsModel({ | |
| 175 | + id: file_uuid, | |
| 176 | + imageUrl: uploadRes?.fileDownloadUri, | |
| 177 | + data: file_json | |
| 178 | + }) | |
| 179 | + const { success } = useMessage() | |
| 180 | + success('保存成功') | |
| 181 | + } finally { | |
| 182 | + stop() | |
| 183 | + } | |
| 176 | 184 | }) |
| 177 | 185 | options.add(option) |
| 178 | 186 | ... | ... |
| ... | ... | @@ -8,10 +8,13 @@ const API = { |
| 8 | 8 | * @description: 3D模型 保存 api |
| 9 | 9 | */ |
| 10 | 10 | export function saveOrUpdateThreeJsModel(params) { |
| 11 | - return defHttp.post({ | |
| 12 | - url: `${API['URL']}?id=${params['id']}&imageUrl=${params['imageUrl']}`, | |
| 13 | - data: params['data'] | |
| 14 | - }) | |
| 11 | + return defHttp.post( | |
| 12 | + { | |
| 13 | + url: `${API['URL']}?id=${params['id']}&imageUrl=${params['imageUrl']}`, | |
| 14 | + data: params['data'], | |
| 15 | + timeout: 30 * 1000 | |
| 16 | + }, | |
| 17 | + ) | |
| 15 | 18 | } |
| 16 | 19 | |
| 17 | 20 | /** | ... | ... |
editor/js/libs/spin/spin.css
0 → 100644
| 1 | +/** | |
| 2 | + * Minified by jsDelivr using clean-css v5.3.2. | |
| 3 | + * Original file: /npm/spin.js@4.1.2/spin.css | |
| 4 | + * | |
| 5 | + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files | |
| 6 | + */ | |
| 7 | +@keyframes spinner-line-fade-more { | |
| 8 | + | |
| 9 | + 0%, | |
| 10 | + 100% { | |
| 11 | + opacity: 0 | |
| 12 | + } | |
| 13 | + | |
| 14 | + 1% { | |
| 15 | + opacity: 1 | |
| 16 | + } | |
| 17 | +} | |
| 18 | + | |
| 19 | +@keyframes spinner-line-fade-quick { | |
| 20 | + | |
| 21 | + 0%, | |
| 22 | + 100%, | |
| 23 | + 39% { | |
| 24 | + opacity: .25 | |
| 25 | + } | |
| 26 | + | |
| 27 | + 40% { | |
| 28 | + opacity: 1 | |
| 29 | + } | |
| 30 | +} | |
| 31 | + | |
| 32 | +@keyframes spinner-line-fade-default { | |
| 33 | + | |
| 34 | + 0%, | |
| 35 | + 100% { | |
| 36 | + opacity: .22 | |
| 37 | + } | |
| 38 | + | |
| 39 | + 1% { | |
| 40 | + opacity: 1 | |
| 41 | + } | |
| 42 | +} | |
| 43 | + | |
| 44 | +@keyframes spinner-line-shrink { | |
| 45 | + | |
| 46 | + 0%, | |
| 47 | + 100%, | |
| 48 | + 25% { | |
| 49 | + transform: scale(.5); | |
| 50 | + opacity: .25 | |
| 51 | + } | |
| 52 | + | |
| 53 | + 26% { | |
| 54 | + transform: scale(1); | |
| 55 | + opacity: 1 | |
| 56 | + } | |
| 57 | +} | |
| 58 | + | |
| 59 | +/*# sourceMappingURL=/sm/1d0379dd77ad25552d64b70cd02d2d2833a1804214c1f482eb413fd2b8c115d4.map */ | ... | ... |
editor/js/libs/spin/spin.js
0 → 100644
| 1 | +/** | |
| 2 | + * Minified by jsDelivr using Terser v5.19.2. | |
| 3 | + * Original file: /npm/spin.js@4.1.2/spin.js | |
| 4 | + * | |
| 5 | + * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files | |
| 6 | + */ | |
| 7 | +var __assign = this && this.__assign || function () { | |
| 8 | + return __assign = Object.assign || function (t) { | |
| 9 | + for (var e, s = 1, n = arguments.length; s < n; s++) | |
| 10 | + for (var i in e = arguments[s]) | |
| 11 | + Object.prototype.hasOwnProperty.call(e, i) && (t[i] = e[i]); | |
| 12 | + return t | |
| 13 | + } | |
| 14 | + , | |
| 15 | + __assign.apply(this, arguments) | |
| 16 | +} | |
| 17 | + , defaults = { | |
| 18 | + lines: 12, | |
| 19 | + length: 7, | |
| 20 | + width: 5, | |
| 21 | + radius: 10, | |
| 22 | + scale: 1, | |
| 23 | + corners: 1, | |
| 24 | + color: "#000", | |
| 25 | + fadeColor: "transparent", | |
| 26 | + animation: "spinner-line-fade-default", | |
| 27 | + rotate: 0, | |
| 28 | + direction: 1, | |
| 29 | + speed: 1, | |
| 30 | + zIndex: 2e9, | |
| 31 | + className: "spinner", | |
| 32 | + top: "50%", | |
| 33 | + left: "50%", | |
| 34 | + shadow: "0 0 1px transparent", | |
| 35 | + position: "absolute" | |
| 36 | + } | |
| 37 | + , Spinner = function () { | |
| 38 | + function t(t) { | |
| 39 | + void 0 === t && (t = {}), | |
| 40 | + this.opts = __assign(__assign({}, defaults), t) | |
| 41 | + } | |
| 42 | + return t.prototype.spin = function (t) { | |
| 43 | + return this.stop(), | |
| 44 | + this.el = document.createElement("div"), | |
| 45 | + this.el.className = this.opts.className, | |
| 46 | + this.el.setAttribute("role", "progressbar"), | |
| 47 | + this.el.style.position = this.opts.position, | |
| 48 | + this.el.style.width = "0", | |
| 49 | + this.el.style.zIndex = this.opts.zIndex.toString(), | |
| 50 | + this.el.style.left = this.opts.left, | |
| 51 | + this.el.style.top = this.opts.top, | |
| 52 | + this.el.style.transform = "scale(".concat(this.opts.scale, ")"), | |
| 53 | + t && t.insertBefore(this.el, t.firstChild || null), | |
| 54 | + drawLines(this.el, this.opts), | |
| 55 | + this | |
| 56 | + } | |
| 57 | + , | |
| 58 | + t.prototype.stop = function () { | |
| 59 | + return this.el && (this.el.parentNode && this.el.parentNode.removeChild(this.el), | |
| 60 | + this.el = void 0), | |
| 61 | + this | |
| 62 | + } | |
| 63 | + , | |
| 64 | + t | |
| 65 | + }(); | |
| 66 | +export { Spinner }; | |
| 67 | +function getColor(t, e) { | |
| 68 | + return "string" == typeof t ? t : t[e % t.length] | |
| 69 | +} | |
| 70 | +function drawLines(t, e) { | |
| 71 | + var s = Math.round(e.corners * e.width * 500) / 1e3 + "px" | |
| 72 | + , n = "none"; | |
| 73 | + !0 === e.shadow ? n = "0 2px 4px #000" : "string" == typeof e.shadow && (n = e.shadow); | |
| 74 | + for (var i = parseBoxShadow(n), o = 0; o < e.lines; o++) { | |
| 75 | + var r = ~~(360 / e.lines * o + e.rotate) | |
| 76 | + , a = document.createElement("div"); | |
| 77 | + a.style.position = "absolute", | |
| 78 | + a.style.top = "".concat(-e.width / 2, "px"), | |
| 79 | + a.style.width = e.length + e.width + "px", | |
| 80 | + a.style.height = e.width + "px", | |
| 81 | + a.style.background = getColor(e.fadeColor, o), | |
| 82 | + a.style.borderRadius = s, | |
| 83 | + a.style.transformOrigin = "left", | |
| 84 | + a.style.transform = "rotate(".concat(r, "deg) translateX(").concat(e.radius, "px)"); | |
| 85 | + var l = o * e.direction / e.lines / e.speed; | |
| 86 | + l -= 1 / e.speed; | |
| 87 | + var h = document.createElement("div"); | |
| 88 | + h.style.width = "100%", | |
| 89 | + h.style.height = "100%", | |
| 90 | + h.style.background = getColor(e.color, o), | |
| 91 | + h.style.borderRadius = s, | |
| 92 | + h.style.boxShadow = normalizeShadow(i, r), | |
| 93 | + h.style.animation = "".concat(1 / e.speed, "s linear ").concat(l, "s infinite ").concat(e.animation), | |
| 94 | + a.appendChild(h), | |
| 95 | + t.appendChild(a) | |
| 96 | + } | |
| 97 | +} | |
| 98 | +function parseBoxShadow(t) { | |
| 99 | + for (var e = /^\s*([a-zA-Z]+\s+)?(-?\d+(\.\d+)?)([a-zA-Z]*)\s+(-?\d+(\.\d+)?)([a-zA-Z]*)(.*)$/, s = [], n = 0, i = t.split(","); n < i.length; n++) { | |
| 100 | + var o = i[n].match(e); | |
| 101 | + if (null !== o) { | |
| 102 | + var r = +o[2] | |
| 103 | + , a = +o[5] | |
| 104 | + , l = o[4] | |
| 105 | + , h = o[7]; | |
| 106 | + 0 !== r || l || (l = h), | |
| 107 | + 0 !== a || h || (h = l), | |
| 108 | + l === h && s.push({ | |
| 109 | + prefix: o[1] || "", | |
| 110 | + x: r, | |
| 111 | + y: a, | |
| 112 | + xUnits: l, | |
| 113 | + yUnits: h, | |
| 114 | + end: o[8] | |
| 115 | + }) | |
| 116 | + } | |
| 117 | + } | |
| 118 | + return s | |
| 119 | +} | |
| 120 | +function normalizeShadow(t, e) { | |
| 121 | + for (var s = [], n = 0, i = t; n < i.length; n++) { | |
| 122 | + var o = i[n] | |
| 123 | + , r = convertOffset(o.x, o.y, e); | |
| 124 | + s.push(o.prefix + r[0] + o.xUnits + " " + r[1] + o.yUnits + o.end) | |
| 125 | + } | |
| 126 | + return s.join(", ") | |
| 127 | +} | |
| 128 | +function convertOffset(t, e, s) { | |
| 129 | + var n = s * Math.PI / 180 | |
| 130 | + , i = Math.sin(n) | |
| 131 | + , o = Math.cos(n); | |
| 132 | + return [Math.round(1e3 * (t * o + e * i)) / 1e3, Math.round(1e3 * (-t * i + e * o)) / 1e3] | |
| 133 | +} | |
| 134 | +//# sourceMappingURL=/sm/6ba007f8215048c7e8c098294081de9cb1287363cf4b90ab212f48050f156783.map | ... | ... |
editor/js/libs/spin/useSpin.js
0 → 100644
| 1 | +import { Spinner } from './spin' | |
| 2 | + | |
| 3 | +export function useSpin(el = 'viewport') { | |
| 4 | + const target = el instanceof Element ? el : document.getElementById(el) | |
| 5 | + const spinner = new Spinner({ color: '#fff', lines: 12 }); | |
| 6 | + const mask = createMask() | |
| 7 | + | |
| 8 | + function createMask() { | |
| 9 | + const mask = document.createElement('div') | |
| 10 | + mask.style.position = 'absolute' | |
| 11 | + mask.style.inset = 0 | |
| 12 | + mask.style.display = 'flex' | |
| 13 | + mask.style.alignItems = 'center' | |
| 14 | + mask.style.justifyContent = 'center' | |
| 15 | + mask.style.backgroundColor = 'rgba(0, 0, 0, 0.5)' | |
| 16 | + mask.style.cursor = 'pointer' | |
| 17 | + mask.style.pointerEvents = 'none' | |
| 18 | + | |
| 19 | + return mask | |
| 20 | + } | |
| 21 | + | |
| 22 | + function spin() { | |
| 23 | + if (!target) { | |
| 24 | + throw new Error('Spin not found target') | |
| 25 | + } | |
| 26 | + | |
| 27 | + target.appendChild(mask) | |
| 28 | + spinner.spin(target) | |
| 29 | + } | |
| 30 | + | |
| 31 | + function stop() { | |
| 32 | + spinner.stop() | |
| 33 | + target.removeChild(mask) | |
| 34 | + } | |
| 35 | + | |
| 36 | + return { | |
| 37 | + spin, | |
| 38 | + stop | |
| 39 | + } | |
| 40 | +} | ... | ... |
| ... | ... | @@ -9,6 +9,7 @@ import { Sidebar } from './js/Sidebar.js' |
| 9 | 9 | import { Menubar } from './js/Menubar.js' |
| 10 | 10 | import { Resizer } from './js/Resizer.js' |
| 11 | 11 | import { getThreeJsModel } from './js/libs/http/api.js' |
| 12 | +import { useSpin } from './js/libs/spin/useSpin.js' | |
| 12 | 13 | |
| 13 | 14 | window.URL = window.URL || window.webkitURL |
| 14 | 15 | window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder || window.MozBlobBuilder |
| ... | ... | @@ -53,10 +54,18 @@ editor.storage.init(async function () { |
| 53 | 54 | const file_uuid = params.get('three_file_uuid') |
| 54 | 55 | const actionType = params.get('action_type') |
| 55 | 56 | if (actionType === 'create') return |
| 56 | - const fileData = await getThreeJsModel(file_uuid) | |
| 57 | - if (!fileData) return | |
| 58 | - if (!fileData['content']) return | |
| 59 | - await editor.fromJSON(fileData['content']) | |
| 57 | + | |
| 58 | + const { spin, stop } = useSpin() | |
| 59 | + try { | |
| 60 | + spin() | |
| 61 | + const fileData = await getThreeJsModel(file_uuid) | |
| 62 | + if (!fileData) return | |
| 63 | + if (!fileData['content']) return | |
| 64 | + await editor.fromJSON(fileData['content']) | |
| 65 | + } finally { | |
| 66 | + stop() | |
| 67 | + } | |
| 68 | + | |
| 60 | 69 | const selected = editor.config.getKey('selected') |
| 61 | 70 | if (selected !== undefined) { |
| 62 | 71 | editor.selectByUuid(selected) |
| ... | ... | @@ -214,5 +223,5 @@ if (hash.slice(1, 6) === 'file=') { |
| 214 | 223 | if ('serviceWorker' in navigator) { |
| 215 | 224 | try { |
| 216 | 225 | navigator.serviceWorker.register('sw.js') |
| 217 | - } catch (error) {} | |
| 226 | + } catch (error) { } | |
| 218 | 227 | } | ... | ... |