mirror of
https://github.com/palxiao/poster-design.git
synced 2025-06-08 03:19:59 +08:00
feat: add generate html feature
This commit is contained in:
parent
14d4005528
commit
ef06021e0b
@ -10,7 +10,8 @@
|
|||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<link rel="icon" href="/favicon.svg" />
|
<link rel="icon" href="/favicon.svg" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<!-- <meta name="viewport" content="width=device-width, initial-scale=1.0" /> -->
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<title>迅排设计 - 轻松创意,迅捷排版,感受云上设计带来的乐趣!</title>
|
<title>迅排设计 - 轻松创意,迅捷排版,感受云上设计带来的乐趣!</title>
|
||||||
<script> var _hmt = _hmt || [] </script>
|
<script> var _hmt = _hmt || [] </script>
|
||||||
</head>
|
</head>
|
||||||
|
@ -7,14 +7,14 @@
|
|||||||
-->
|
-->
|
||||||
<template>
|
<template>
|
||||||
<div id="main">
|
<div id="main">
|
||||||
<div id="page-design" ref="page_design" :style="{ paddingTop: dPaddingTop + 'px', minWidth: (dPage.width * dZoom) / 100 + dPresetPadding * 2 + 'px' }">
|
<div id="page-design" ref="page_design" :style="{ paddingTop: dPaddingTop + 'px', minWidth: (dPage.width * dZoom) / 100 + (padding ?? dPresetPadding) * 2 + 'px' }">
|
||||||
<div
|
<div
|
||||||
id="out-page"
|
id="out-page"
|
||||||
class="out-page"
|
class="out-page"
|
||||||
:style="{
|
:style="{
|
||||||
padding: dPresetPadding + 'px',
|
padding: padding ?? dPresetPadding + 'px',
|
||||||
width: (dPage.width * dZoom) / 100 + dPresetPadding * 2 + 'px',
|
width: (dPage.width * dZoom) / 100 + (padding ?? dPresetPadding) * 2 + 'px',
|
||||||
height: (dPage.height * dZoom) / 100 + dPresetPadding * 2 + 'px',
|
height: (dPage.height * dZoom) / 100 + (padding ?? dPresetPadding) * 2 + 'px',
|
||||||
opacity: 1 - (dZoom < 100 ? dPage.tag : 0),
|
opacity: 1 - (dZoom < 100 ? dPage.tag : 0),
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
@ -22,7 +22,7 @@
|
|||||||
<resize-page :width="(dPage.width * dZoom) / 100" :height="(dPage.height * dZoom) / 100" />
|
<resize-page :width="(dPage.width * dZoom) / 100" :height="(dPage.height * dZoom) / 100" />
|
||||||
<watermark :customStyle="{ height: (dPage.height * dZoom) / 100 + 'px' }">
|
<watermark :customStyle="{ height: (dPage.height * dZoom) / 100 + 'px' }">
|
||||||
<div
|
<div
|
||||||
:id="pageDesignCanvasId"
|
:id="props.pageDesignCanvasId"
|
||||||
class="design-canvas"
|
class="design-canvas"
|
||||||
:data-type="dPage.type"
|
:data-type="dPage.type"
|
||||||
:data-uuid="dPage.uuid"
|
:data-uuid="dPage.uuid"
|
||||||
@ -66,7 +66,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { onMounted } from 'vue'
|
import { computed, onMounted } from 'vue'
|
||||||
import { getTarget } from '@/common/methods/target'
|
import { getTarget } from '@/common/methods/target'
|
||||||
import setWidgetData from '@/common/methods/DesignFeatures/setWidgetData'
|
import setWidgetData from '@/common/methods/DesignFeatures/setWidgetData'
|
||||||
import PointImg from '@/utils/plugins/pointImg'
|
import PointImg from '@/utils/plugins/pointImg'
|
||||||
@ -78,10 +78,17 @@ import { storeToRefs } from 'pinia'
|
|||||||
import { TPageState } from '@/store/design/canvas/d'
|
import { TPageState } from '@/store/design/canvas/d'
|
||||||
import resizePage from './comps/resize.vue'
|
import resizePage from './comps/resize.vue'
|
||||||
import watermark from './comps/pageWatermark.vue'
|
import watermark from './comps/pageWatermark.vue'
|
||||||
|
import { TdWidgetData } from '@/store/design/widget'
|
||||||
|
|
||||||
// 页面设计组件
|
// 页面设计组件
|
||||||
type TProps = {
|
type TProps = {
|
||||||
pageDesignCanvasId: string
|
pageDesignCanvasId: string
|
||||||
|
/** 以下参数仅用于图片渲染html */
|
||||||
|
padding?: number
|
||||||
|
/** 用于生成渲染图片 */
|
||||||
|
renderDPage?: TPageState
|
||||||
|
renderDWdigets?: TdWidgetData[]
|
||||||
|
zoom?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type TParentData = {
|
type TParentData = {
|
||||||
@ -95,12 +102,16 @@ const controlStore = useControlStore()
|
|||||||
const widgetStore = useWidgetStore()
|
const widgetStore = useWidgetStore()
|
||||||
const canvasStore = useCanvasStore()
|
const canvasStore = useCanvasStore()
|
||||||
|
|
||||||
const { pageDesignCanvasId } = defineProps<TProps>()
|
const props = defineProps<TProps>()
|
||||||
|
|
||||||
const { dPage } = storeToRefs(useCanvasStore())
|
const { dPage: curDPage } = storeToRefs(useCanvasStore())
|
||||||
const { dZoom, dPresetPadding, dPaddingTop, dScreen } = storeToRefs(canvasStore)
|
const { dZoom: curZoom, dPresetPadding, dPaddingTop, dScreen } = storeToRefs(canvasStore)
|
||||||
const { dDraging, showRotatable, dAltDown, dSpaceDown } = storeToRefs(controlStore)
|
const { dDraging, showRotatable, dAltDown, dSpaceDown } = storeToRefs(controlStore)
|
||||||
const { dWidgets, dActiveElement, dSelectWidgets, dHoverUuid } = storeToRefs(widgetStore)
|
const { dWidgets: curWidgets, dActiveElement, dSelectWidgets, dHoverUuid } = storeToRefs(widgetStore)
|
||||||
|
|
||||||
|
const dPage = computed(() => props.renderDPage ?? curDPage.value)
|
||||||
|
const dWidgets = computed(() => props.renderDWdigets ?? curWidgets.value)
|
||||||
|
const dZoom = computed(() => props.zoom ?? curZoom.value)
|
||||||
|
|
||||||
let _dropIn: string | null = ''
|
let _dropIn: string | null = ''
|
||||||
let _srcCache: string | null = ''
|
let _srcCache: string | null = ''
|
||||||
|
@ -37,6 +37,11 @@ export default [
|
|||||||
name: 'Draw',
|
name: 'Draw',
|
||||||
component: () => import(/* webpackChunkName: 'draw' */ '@/views/Draw.vue'),
|
component: () => import(/* webpackChunkName: 'draw' */ '@/views/Draw.vue'),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "/html",
|
||||||
|
name: "Html",
|
||||||
|
component: () => import(/* webpackChunkName: 'html' */ '@/views/Html.vue'),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/psd',
|
path: '/psd',
|
||||||
name: 'Psd',
|
name: 'Psd',
|
||||||
|
197
src/views/Html.vue
Normal file
197
src/views/Html.vue
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
<!--
|
||||||
|
* @Author: Jeremy Yu
|
||||||
|
* @Date: 2024-12-27 00:02:46
|
||||||
|
* @Description: 图片生成HTML页面
|
||||||
|
* @LastEditors: Jeremy Yu <https://book.yzmblog.top>
|
||||||
|
* @LastEditTime: 2024-12-28 12:28:00
|
||||||
|
-->
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div ref="pageDesignIndex">
|
||||||
|
<div class="page-design-index-wrap" id="page-draw-html-wrap">
|
||||||
|
<template v-for="x in pageGroup" :key="x.pageData.uuid">
|
||||||
|
<design-board
|
||||||
|
class="page-design-wrap fixed-canvas"
|
||||||
|
pageDesignCanvasId="page-design-canvas"
|
||||||
|
:padding="0"
|
||||||
|
:renderDWdigets="x.dWidgets"
|
||||||
|
:renderDPage="x.pageData"
|
||||||
|
:zoom="x.zoom * 100"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { onMounted, nextTick, ref, onUnmounted } from 'vue'
|
||||||
|
import api from '@/api'
|
||||||
|
import Preload from '@/utils/plugins/preload'
|
||||||
|
import FontFaceObserver from 'fontfaceobserver'
|
||||||
|
import { fontWithDraw, font2style } from '@/utils/widgets/loadFontRule'
|
||||||
|
import designBoard from '@/components/modules/layout/designBoard/index.vue'
|
||||||
|
import { useRoute } from 'vue-router'
|
||||||
|
import { TPageState } from '@/store/design/canvas/d'
|
||||||
|
import { TdWidgetData } from '@/store/design/widget'
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
|
||||||
|
const pageGroup = ref<{pageData: TPageState, dWidgets: TdWidgetData[], zoom: number}[]>([])
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
nextTick(() => {
|
||||||
|
load()
|
||||||
|
window.addEventListener("resize", handleResize, false);
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener("resize", handleResize, false);
|
||||||
|
})
|
||||||
|
|
||||||
|
async function load() {
|
||||||
|
let backgroundImage = ''
|
||||||
|
let loadFlag = false
|
||||||
|
const { id, tempid }: any = route.query
|
||||||
|
if (id || tempid) {
|
||||||
|
const postData = {
|
||||||
|
id: id || tempid
|
||||||
|
}
|
||||||
|
const { data } = await api.home[id ? 'getWorks' : 'getTempDetail'](postData)
|
||||||
|
let contentGroup = JSON.parse(data)
|
||||||
|
|
||||||
|
contentGroup = Array.isArray(contentGroup) ? contentGroup : [contentGroup]
|
||||||
|
|
||||||
|
for (let i = 0; i < contentGroup.length; i++) {
|
||||||
|
const { global, layers } = contentGroup[i]
|
||||||
|
let content = {page: global, widgets: layers}
|
||||||
|
|
||||||
|
const widgets = content.widgets
|
||||||
|
const zoom = controlScale(content.page?.width)
|
||||||
|
|
||||||
|
// 移除背景图,作为独立事件
|
||||||
|
backgroundImage = content.page?.backgroundImage
|
||||||
|
backgroundImage && delete content.page.backgroundImage
|
||||||
|
|
||||||
|
await nextTick()
|
||||||
|
|
||||||
|
pageGroup.value.push({
|
||||||
|
pageData: content.page,
|
||||||
|
dWidgets: widgets,
|
||||||
|
zoom,
|
||||||
|
})
|
||||||
|
|
||||||
|
const imgsData: HTMLImageElement[] = []
|
||||||
|
const svgsData: HTMLImageElement[] = []
|
||||||
|
const fontLoaders: Promise<void>[] = []
|
||||||
|
const fontContent: Record<string, string> = {}
|
||||||
|
let fontData: string[] = []
|
||||||
|
widgets.forEach((item: any) => {
|
||||||
|
if (item.fontClass && item.fontClass.value) {
|
||||||
|
const loader = new FontFaceObserver(item.fontClass.value)
|
||||||
|
fontData.push(item.fontClass)
|
||||||
|
fontLoaders.push(loader.load(null, 30000)) // 延长超时让检测不会丢失字体
|
||||||
|
// 按字体来收集所有文字
|
||||||
|
if (fontContent[item.fontClass.value]) {
|
||||||
|
fontContent[item.fontClass.value] += item.text
|
||||||
|
} else {
|
||||||
|
fontContent[item.fontClass.value] = item.text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 收集图片元素、svg元素
|
||||||
|
try {
|
||||||
|
if (item.svgUrl && item.type === 'w-svg') {
|
||||||
|
const cNodes: any = (window as any).document.getElementById(item.uuid).childNodes
|
||||||
|
svgsData.push(cNodes)
|
||||||
|
} else if (item.imgUrl && !item.isNinePatch) {
|
||||||
|
const cNodes: any = (window as any).document.getElementById(item.uuid).childNodes
|
||||||
|
for (const el of cNodes) {
|
||||||
|
if (el.className && el.className.includes('img__box')) {
|
||||||
|
imgsData.push(el.firstChild)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {}
|
||||||
|
})
|
||||||
|
// 背景图无法检测是否加载完毕,所以单独做判断
|
||||||
|
if (backgroundImage) {
|
||||||
|
const preloadBg = new Preload([backgroundImage])
|
||||||
|
await preloadBg.imgs()
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
fontWithDraw && (await font2style(fontContent, fontData))
|
||||||
|
// console.log('1. base64 yes')
|
||||||
|
const preload = new Preload(imgsData)
|
||||||
|
await preload.doms()
|
||||||
|
// console.log('2. image yes')
|
||||||
|
const preload2 = new Preload(svgsData)
|
||||||
|
await preload2.svgs()
|
||||||
|
// console.log('3. svg yes')
|
||||||
|
} catch (e) {
|
||||||
|
console.log(e)
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
await Promise.all(fontLoaders)
|
||||||
|
// console.log('4. font yes')
|
||||||
|
} catch (e) {
|
||||||
|
// console.log(e)
|
||||||
|
}
|
||||||
|
loadFlag = true
|
||||||
|
console.log('--> now u can start generate artboard to html!')
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
;(window as any).loadFinishToInject('done')
|
||||||
|
} catch (err) {}
|
||||||
|
}, 100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 超时
|
||||||
|
setTimeout(() => {
|
||||||
|
!loadFlag && (window as any).loadFinishToInject('done')
|
||||||
|
}, 60000)
|
||||||
|
}
|
||||||
|
|
||||||
|
function controlScale(width: number) {
|
||||||
|
const winWidth = document.documentElement.clientWidth
|
||||||
|
let curZoom = (winWidth / width);
|
||||||
|
curZoom = curZoom > 1 ? 1 : curZoom
|
||||||
|
|
||||||
|
return curZoom
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleResize() {
|
||||||
|
pageGroup.value = pageGroup.value.map(val => {
|
||||||
|
val.zoom = controlScale(val.pageData.width);
|
||||||
|
return val
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="less" scoped>
|
||||||
|
@import url('@/assets/styles/design.less');
|
||||||
|
|
||||||
|
#page-draw-html-wrap {
|
||||||
|
width: 100vw;
|
||||||
|
height: 100vh;
|
||||||
|
overflow: scroll;
|
||||||
|
offset: 0px;
|
||||||
|
|
||||||
|
scrollbar-width: none; /* Firefox 隐藏滚动条 */
|
||||||
|
#main {
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#page-draw-html-wrap::-webkit-scrollbar {
|
||||||
|
display: none; /* WebKit 浏览器隐藏滚动条 */
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
.layer-hover {
|
||||||
|
outline: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
Loading…
x
Reference in New Issue
Block a user