Commit 857d5b2bfdb49296c51e66e4a4ecd966c9cf517b

Authored by ww
1 parent 59995619

perf: 优化editor保存与加载loading

... ... @@ -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 /**
... ...
  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 */
... ...
  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
... ...
  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 }
... ...