添加vue-canvas-poster
This commit is contained in:
@ -1,91 +1,100 @@
|
||||
<template>
|
||||
<div>
|
||||
<template>
|
||||
<div style="width:100%">
|
||||
<el-upload
|
||||
:action="useOss?ossUploadUrl:minioUploadUrl"
|
||||
:data="useOss?dataObj:null"
|
||||
:action="useOss ? ossUploadUrl : minioUploadUrl"
|
||||
:data="useOss ? dataObj : null"
|
||||
list-type="picture"
|
||||
:multiple="false" :show-file-list="showFileList"
|
||||
:multiple="false"
|
||||
:show-file-list="showFileList"
|
||||
:file-list="fileList"
|
||||
:before-upload="beforeUpload"
|
||||
:on-remove="handleRemove"
|
||||
:on-success="handleUploadSuccess"
|
||||
:on-preview="handlePreview">
|
||||
<el-button size="small" type="primary">点击上传</el-button>
|
||||
<div slot="tip" class="el-upload__tip">只能上传jpg/png文件,且不超过10MB</div>
|
||||
:on-preview="handlePreview"
|
||||
>
|
||||
<el-button size="mini" style="width:100%">添加图片</el-button>
|
||||
<!-- <div slot="tip" class="el-upload__tip">
|
||||
只能上传jpg/png文件,且不超过10MB
|
||||
</div> -->
|
||||
</el-upload>
|
||||
<el-dialog :visible.sync="dialogVisible">
|
||||
<img width="100%" :src="fileList[0].url" alt="">
|
||||
</el-dialog>
|
||||
<!-- <el-dialog :visible.sync="dialogVisible">
|
||||
<img width="100%" :src="fileList[0].url" alt="" />
|
||||
</el-dialog> -->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {policy} from '@/api/oss'
|
||||
import { policy } from '@/api/oss';
|
||||
|
||||
export default {
|
||||
name: 'singleUpload',
|
||||
props: {
|
||||
value: String
|
||||
export default {
|
||||
name: 'singleUpload',
|
||||
props: {
|
||||
value: String
|
||||
},
|
||||
computed: {
|
||||
imageUrl() {
|
||||
return this.value;
|
||||
},
|
||||
computed: {
|
||||
imageUrl() {
|
||||
return this.value;
|
||||
},
|
||||
imageName() {
|
||||
if (this.value != null && this.value !== '') {
|
||||
return this.value.substr(this.value.lastIndexOf("/") + 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
fileList() {
|
||||
return [{
|
||||
name: this.imageName,
|
||||
url: this.imageUrl
|
||||
}]
|
||||
},
|
||||
showFileList: {
|
||||
get: function () {
|
||||
return this.value !== null && this.value !== ''&& this.value!==undefined;
|
||||
},
|
||||
set: function (newValue) {
|
||||
}
|
||||
imageName() {
|
||||
if (this.value != null && this.value !== '') {
|
||||
return this.value.substr(this.value.lastIndexOf('/') + 1);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataObj: {
|
||||
policy: '',
|
||||
signature: '',
|
||||
key: '',
|
||||
ossaccessKeyId: '',
|
||||
dir: '',
|
||||
host: '',
|
||||
// callback:'',
|
||||
},
|
||||
dialogVisible: false,
|
||||
useOss:false, //使用oss->true;使用MinIO->false
|
||||
ossUploadUrl:'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
|
||||
minioUploadUrl:'http://192.168.99.239:1818/minio/upload',
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
emitInput(val) {
|
||||
this.$emit('input', val)
|
||||
},
|
||||
handleRemove(file, fileList) {
|
||||
this.emitInput('');
|
||||
},
|
||||
handlePreview(file) {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
beforeUpload(file) {
|
||||
let _self = this;
|
||||
if(!this.useOss){
|
||||
//不使用oss不需要获取策略
|
||||
return true;
|
||||
fileList() {
|
||||
return [
|
||||
{
|
||||
name: this.imageName,
|
||||
url: this.imageUrl
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
policy().then(response => {
|
||||
];
|
||||
},
|
||||
showFileList: {
|
||||
get: function() {
|
||||
return (
|
||||
this.value !== null && this.value !== '' && this.value !== undefined
|
||||
);
|
||||
},
|
||||
set: function(newValue) {}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
dataObj: {
|
||||
policy: '',
|
||||
signature: '',
|
||||
key: '',
|
||||
ossaccessKeyId: '',
|
||||
dir: '',
|
||||
host: ''
|
||||
// callback:'',
|
||||
},
|
||||
dialogVisible: false,
|
||||
useOss: false, //使用oss->true;使用MinIO->false
|
||||
ossUploadUrl: 'http://macro-oss.oss-cn-shenzhen.aliyuncs.com',
|
||||
// minioUploadUrl:'http://192.168.99.239:1818/minio/upload',
|
||||
minioUploadUrl: process.env.VUE_APP_BASE_API + '/minio/upload'
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
emitInput(val) {
|
||||
this.$emit('input', val);
|
||||
},
|
||||
handleRemove(file, fileList) {
|
||||
this.emitInput('');
|
||||
},
|
||||
handlePreview(file) {
|
||||
this.dialogVisible = true;
|
||||
},
|
||||
beforeUpload(file) {
|
||||
let _self = this;
|
||||
if (!this.useOss) {
|
||||
//不使用oss不需要获取策略
|
||||
return true;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
policy()
|
||||
.then(response => {
|
||||
_self.dataObj.policy = response.data.policy;
|
||||
_self.dataObj.signature = response.data.signature;
|
||||
_self.dataObj.ossaccessKeyId = response.data.accessKeyId;
|
||||
@ -93,29 +102,26 @@
|
||||
_self.dataObj.dir = response.data.dir;
|
||||
_self.dataObj.host = response.data.host;
|
||||
// _self.dataObj.callback = response.data.callback;
|
||||
resolve(true)
|
||||
}).catch(err => {
|
||||
console.log(err)
|
||||
reject(false)
|
||||
resolve(true);
|
||||
})
|
||||
})
|
||||
},
|
||||
handleUploadSuccess(res, file) {
|
||||
this.showFileList = true;
|
||||
this.fileList.pop();
|
||||
let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
|
||||
if(!this.useOss){
|
||||
//不使用oss直接获取图片路径
|
||||
url = res.data.url;
|
||||
}
|
||||
this.fileList.push({name: file.name, url: url});
|
||||
this.emitInput(this.fileList[0].url);
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
reject(false);
|
||||
});
|
||||
});
|
||||
},
|
||||
handleUploadSuccess(res, file) {
|
||||
this.showFileList = true;
|
||||
this.fileList.pop();
|
||||
let url = this.dataObj.host + '/' + this.dataObj.dir + '/' + file.name;
|
||||
if (!this.useOss) {
|
||||
//不使用oss直接获取图片路径
|
||||
url = res.data.url;
|
||||
}
|
||||
this.fileList.push({ name: file.name, url: url });
|
||||
this.emitInput(this.fileList[0].url);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style>
|
||||
|
||||
</style>
|
||||
|
||||
|
||||
<style></style>
|
||||
|
||||
109
src/components/blankDialog/blankDialog.vue
Normal file
109
src/components/blankDialog/blankDialog.vue
Normal file
@ -0,0 +1,109 @@
|
||||
<template>
|
||||
<div class="blankDialog-comp-ct">
|
||||
<el-dialog
|
||||
@close="handleClose"
|
||||
:width="contentWidth+'px'"
|
||||
:visible.sync="showDialog">
|
||||
<div
|
||||
class="content"
|
||||
:style="{
|
||||
background: bg,
|
||||
height: contentHeight + 'px',
|
||||
contentWidth: contentWidth + 'px',
|
||||
padding: padding + 'px'
|
||||
}"
|
||||
>
|
||||
<div class="title">{{ dTitle }}</div>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
name: 'blankDialog',
|
||||
props: {
|
||||
bg: {
|
||||
type: String,
|
||||
default: '#fff'
|
||||
},
|
||||
contentWidth: {
|
||||
type: Number,
|
||||
default: 720
|
||||
},
|
||||
padding: {
|
||||
type: Number,
|
||||
default: 48
|
||||
},
|
||||
contentHeight: {
|
||||
type: Number,
|
||||
default: 500
|
||||
},
|
||||
value: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dTitle: {
|
||||
type: String
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
showDialog: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClose () {
|
||||
this.$emit('close')
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if (val !== this.showDialog) {
|
||||
this.showDialog = val
|
||||
}
|
||||
},
|
||||
showDialog (val) {
|
||||
if (val !== this.value) {
|
||||
this.$emit('input', val)
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.showDialog = this.value
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.blankDialog-comp-ct{
|
||||
.el-dialog{
|
||||
/* width: 800px;*/
|
||||
.el-dialog__header{
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
.el-dialog__headerbtn .el-dialog__close{
|
||||
color: #282828;
|
||||
}
|
||||
}
|
||||
.el-dialog__body{
|
||||
padding: 0;
|
||||
height: auto;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
<style lang="scss" scoped>
|
||||
.blankDialog-comp-ct {
|
||||
.content{
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
/*padding: 48px;*/
|
||||
.title{
|
||||
margin-bottom: 16px;
|
||||
font-size: 24px;
|
||||
color: rgba(33, 33, 33, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
6
src/components/blankDialog/index.js
Normal file
6
src/components/blankDialog/index.js
Normal file
@ -0,0 +1,6 @@
|
||||
import blankDialog from './blankDialog.vue'
|
||||
import Vue from 'vue'
|
||||
blankDialog.install = function () {
|
||||
Vue.component(blankDialog.name, blankDialog)
|
||||
}
|
||||
export default blankDialog
|
||||
239
src/components/imageSelection.vue
Normal file
239
src/components/imageSelection.vue
Normal file
@ -0,0 +1,239 @@
|
||||
<template>
|
||||
<div class="image-selection-comp">
|
||||
<div class="header-ct m-b-16">
|
||||
<div class="title">选择图片</div>
|
||||
<div class="right">
|
||||
<el-input
|
||||
clearable
|
||||
v-model="search"
|
||||
placeholader="搜索内容"
|
||||
class="width-200" size="mini" suffix-icon="el-icon-search">
|
||||
<el-button slot="append">搜索</el-button>
|
||||
</el-input>
|
||||
</div>
|
||||
</div>
|
||||
<div class="main">
|
||||
<div class="right">
|
||||
<div
|
||||
@click="handleClickImageItem(i, item)"
|
||||
class="list-item cursor-p"
|
||||
:key="item.uid"
|
||||
v-for="(item, i) in imageList"
|
||||
:style="{background: item.bg}">
|
||||
<el-checkbox class="check-ct" :value="item.active"></el-checkbox>
|
||||
<img :src="item.path" class="img"/>
|
||||
<div class="text single-row-ellipsis">
|
||||
{{item.name}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bottom-ct">
|
||||
<el-button @click="cancel">取消</el-button>
|
||||
<el-button @click="submit" type="primary">确定</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import uuid from 'uuid'
|
||||
export default {
|
||||
props: {
|
||||
// 是否单选
|
||||
isSingle: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
value: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
search: '',
|
||||
imageList: [
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '钢铁侠',
|
||||
path: require('../assets/image/1.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '蜘蛛侠',
|
||||
path: require('../assets/image/2.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '美国队长',
|
||||
path: require('../assets/image/3.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '奇异博士',
|
||||
path: require('../assets/image/4.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '蚁人',
|
||||
path: require('../assets/image/5.jpg')
|
||||
},
|
||||
{
|
||||
uid: uuid(),
|
||||
name: '钢铁侠2',
|
||||
path: require('../assets/image/6.jpg')
|
||||
}
|
||||
],
|
||||
activeIndex: -1,
|
||||
selectionImage: [],
|
||||
selectionImageUids: [],
|
||||
systemMaterialList: [],
|
||||
materialList: [],
|
||||
tagSelect: ''
|
||||
// systemMaterialListCount: 0,
|
||||
// materialListCount: 0
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClickImageItem (i, item) {
|
||||
this.isSingle ? this.singleModeClick(i, item) : this.multipleModeClick(i, item)
|
||||
},
|
||||
singleModeClick (i, item) {
|
||||
if (i === this.activeIndex) {
|
||||
this.$set(this.imageList, i, { ...item, active: false })
|
||||
this.activeIndex = -1
|
||||
} else {
|
||||
let oldItem = this.imageList[this.activeIndex]
|
||||
this.$set(this.imageList, this.activeIndex, { ...oldItem, active: false })
|
||||
this.activeIndex = i
|
||||
this.$set(this.imageList, this.activeIndex, { ...item, active: true })
|
||||
}
|
||||
},
|
||||
multipleModeClick (i, item) {
|
||||
this.$set(this.imageList, i, { ...item, active: !item.active })
|
||||
},
|
||||
reset () {
|
||||
this.imageList.forEach(v => {
|
||||
v.active = false
|
||||
})
|
||||
},
|
||||
async submit () {
|
||||
let imageSelection = this.imageList.filter(v => v.active)
|
||||
if (imageSelection.length === 0) {
|
||||
this.$message.error('请选择图片!')
|
||||
return false
|
||||
}
|
||||
this.$emit('success', this.isSingle ? imageSelection[0] : imageSelection)
|
||||
this.reset()
|
||||
},
|
||||
cancel () {
|
||||
this.$emit('cancel')
|
||||
this.reset()
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if (val) {
|
||||
// this.findBackground()
|
||||
}
|
||||
}
|
||||
},
|
||||
async created () {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.image-selection-comp{
|
||||
position: relative;
|
||||
.header-ct{
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
.title {
|
||||
font-size: 27px;
|
||||
font-weight: 400;
|
||||
color: rgba(44, 62, 80, 1);
|
||||
}
|
||||
.right{
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
.main{
|
||||
display: flex;
|
||||
.left{
|
||||
box-sizing: border-box;
|
||||
width: 200px;
|
||||
height: 400px;
|
||||
border-right: 1px solid #E4E8EB;
|
||||
.type-child-item{
|
||||
position: relative;
|
||||
padding-left: 24px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
&.active{
|
||||
background: #409EFF;
|
||||
color: white;
|
||||
&:before{
|
||||
background: white;
|
||||
}
|
||||
}
|
||||
&:before{
|
||||
content: '';
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 13px;
|
||||
left: 8px;
|
||||
background: black;
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
overflow: hidden;
|
||||
border-radius: 50%;
|
||||
z-index: 9;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right{
|
||||
flex: 1;
|
||||
display: grid;
|
||||
flex-wrap: wrap;
|
||||
grid-template-columns: repeat(auto-fill, 144px);
|
||||
grid-template-rows: repeat(auto-fill, 144px);
|
||||
grid-row-gap: 8px;
|
||||
grid-column-gap: 8px;
|
||||
margin-left: 16px;
|
||||
.list-item{
|
||||
position: relative;
|
||||
width: 144px;
|
||||
height: 144px;
|
||||
border: 1px solid #e1e1e1;
|
||||
overflow: hidden;
|
||||
.check-ct{
|
||||
position: absolute;
|
||||
top: 8px;
|
||||
left: 8px;
|
||||
}
|
||||
.img{
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100.8px;
|
||||
}
|
||||
}
|
||||
.text{
|
||||
height: 43px;
|
||||
line-height: 43px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
}
|
||||
.bottom-ct{
|
||||
/*position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;*/
|
||||
margin-top: 32px;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
93
src/components/poster-editor/alignSelect.vue
Normal file
93
src/components/poster-editor/alignSelect.vue
Normal file
@ -0,0 +1,93 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tooltip
|
||||
:key="item.icon"
|
||||
v-for="item in iconList"
|
||||
effect="dark"
|
||||
:content="item.label"
|
||||
placement="top-start">
|
||||
<Icon
|
||||
:active="(item.dir === 'x' && item.value === align.x) || (item.dir === 'y' && item.value === align.y)"
|
||||
@click.native="handleClick(item)" :icon="item.icon"></Icon>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import _ from 'lodash'
|
||||
import Icon from './icon'
|
||||
export default {
|
||||
components: {
|
||||
Icon
|
||||
},
|
||||
props: {
|
||||
value: {
|
||||
type: Object,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
align: {
|
||||
x: 2,
|
||||
y: 2
|
||||
},
|
||||
iconList: [
|
||||
{
|
||||
icon: 'zuoduiqi2',
|
||||
label: '左对齐',
|
||||
value: 1,
|
||||
dir: 'x'
|
||||
},
|
||||
{
|
||||
icon: 'shuipingjuzhongduiqi',
|
||||
label: '水平居中对齐',
|
||||
value: 2,
|
||||
dir: 'x'
|
||||
},
|
||||
{
|
||||
icon: 'youduiqi',
|
||||
label: '右对齐',
|
||||
value: 3,
|
||||
dir: 'x'
|
||||
},
|
||||
{
|
||||
icon: 'shangduiqi',
|
||||
label: '上对齐',
|
||||
value: 1,
|
||||
dir: 'y'
|
||||
},
|
||||
{
|
||||
icon: 'chuizhijuzhongduiqi',
|
||||
label: '垂直居中对齐',
|
||||
value: 2,
|
||||
dir: 'y'
|
||||
},
|
||||
{
|
||||
icon: 'xiaduiqi',
|
||||
label: '下对齐',
|
||||
value: 3,
|
||||
dir: 'y'
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleClick (item) {
|
||||
this.align[item.dir] = item.value
|
||||
this.$emit('input', this.align)
|
||||
}
|
||||
},
|
||||
created () {
|
||||
this.align = { ...this.value }
|
||||
},
|
||||
watch: {
|
||||
value (val) {
|
||||
if (!_.isEqual(val, this.align)) {
|
||||
this.align = { ...val }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
539
src/components/poster-editor/icon/demo.css
Normal file
539
src/components/poster-editor/icon/demo.css
Normal file
@ -0,0 +1,539 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
||||
446
src/components/poster-editor/icon/demo_index.html
Normal file
446
src/components/poster-editor/icon/demo_index.html
Normal file
@ -0,0 +1,446 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>IconFont Demo</title>
|
||||
<link rel="shortcut icon" href="https://gtms04.alicdn.com/tps/i4/TB1_oz6GVXXXXaFXpXXJDFnIXXX-64-64.ico" type="image/x-icon"/>
|
||||
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
|
||||
<link rel="stylesheet" href="demo.css">
|
||||
<link rel="stylesheet" href="iconfont.css">
|
||||
<script src="iconfont.js"></script>
|
||||
<!-- jQuery -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
|
||||
<!-- 代码高亮 -->
|
||||
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="main">
|
||||
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank"></a></h1>
|
||||
<div class="nav-tabs">
|
||||
<ul id="tabs" class="dib-box">
|
||||
<li class="dib active"><span>Unicode</span></li>
|
||||
<li class="dib"><span>Font class</span></li>
|
||||
<li class="dib"><span>Symbol</span></li>
|
||||
</ul>
|
||||
|
||||
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=1577785" target="_blank" class="nav-more">查看项目</a>
|
||||
|
||||
</div>
|
||||
<div class="tab-container">
|
||||
<div class="content unicode" style="display: block;">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">&#xe65b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">下对齐</div>
|
||||
<div class="code-name">&#xe64b;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">水平居中对齐</div>
|
||||
<div class="code-name">&#xe600;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">&#xe67f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">居中对齐</div>
|
||||
<div class="code-name">&#xe632;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">上对齐</div>
|
||||
<div class="code-name">&#xe64e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">&#xe650;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">&#xe60e;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">&#xe60f;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">&#xe603;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">垂直居中对齐</div>
|
||||
<div class="code-name">&#xe670;</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont"></span>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">&#xe651;</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="unicode-">Unicode 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
|
||||
<ul>
|
||||
<li>兼容性最好,支持 IE6+,及所有现代浏览器。</li>
|
||||
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
|
||||
<li>但是因为是字体,所以不支持多色。只能使用平台里单色的图标,就算项目里有多色图标也会自动去色。</li>
|
||||
</ul>
|
||||
<blockquote>
|
||||
<p>注意:新版 iconfont 支持多色图标,这些多色图标在 Unicode 模式下将不能使用,如果有需求建议使用symbol 的引用方式</p>
|
||||
</blockquote>
|
||||
<p>Unicode 使用步骤如下:</p>
|
||||
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
|
||||
<pre><code class="language-css"
|
||||
>@font-face {
|
||||
font-family: 'iconfont';
|
||||
src: url('iconfont.eot');
|
||||
src: url('iconfont.eot?#iefix') format('embedded-opentype'),
|
||||
url('iconfont.woff2') format('woff2'),
|
||||
url('iconfont.woff') format('woff'),
|
||||
url('iconfont.ttf') format('truetype'),
|
||||
url('iconfont.svg#iconfont') format('svg');
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
|
||||
<pre><code class="language-css"
|
||||
>.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
|
||||
<pre>
|
||||
<code class="language-html"
|
||||
><span class="iconfont">&#x33;</span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content font-class">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-youduiqi"></span>
|
||||
<div class="name">
|
||||
右对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-youduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-xiaduiqi"></span>
|
||||
<div class="name">
|
||||
下对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-xiaduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shuipingjuzhongduiqi"></span>
|
||||
<div class="name">
|
||||
水平居中对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-shuipingjuzhongduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zuoduiqi"></span>
|
||||
<div class="name">
|
||||
左对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-zuoduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-card-text-center"></span>
|
||||
<div class="name">
|
||||
居中对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-card-text-center
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-shangduiqi"></span>
|
||||
<div class="name">
|
||||
上对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-shangduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zuoduiqi2"></span>
|
||||
<div class="name">
|
||||
左对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-zuoduiqi2
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-youduiqi1"></span>
|
||||
<div class="name">
|
||||
右对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-youduiqi1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-zuoduiqi1"></span>
|
||||
<div class="name">
|
||||
左对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-zuoduiqi1
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-Undo"></span>
|
||||
<div class="name">
|
||||
undo
|
||||
</div>
|
||||
<div class="code-name">.icon-Undo
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-chuizhijuzhongduiqi"></span>
|
||||
<div class="name">
|
||||
垂直居中对齐
|
||||
</div>
|
||||
<div class="code-name">.icon-chuizhijuzhongduiqi
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<span class="icon iconfont icon-ic_undo"></span>
|
||||
<div class="name">
|
||||
undo
|
||||
</div>
|
||||
<div class="code-name">.icon-ic_undo
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="font-class-">font-class 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
|
||||
<p>与 Unicode 使用方式相比,具有如下特点:</p>
|
||||
<ul>
|
||||
<li>兼容性良好,支持 IE8+,及所有现代浏览器。</li>
|
||||
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
|
||||
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
|
||||
<li>不过因为本质上还是使用的字体,所以多色图标还是不支持的。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
|
||||
<pre><code class="language-html"><link rel="stylesheet" href="./iconfont.css">
|
||||
</code></pre>
|
||||
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><span class="iconfont icon-xxx"></span>
|
||||
</code></pre>
|
||||
<blockquote>
|
||||
<p>"
|
||||
iconfont" 是你项目下的 font-family。可以通过编辑项目查看,默认是 "iconfont"。</p>
|
||||
</blockquote>
|
||||
</div>
|
||||
</div>
|
||||
<div class="content symbol">
|
||||
<ul class="icon_lists dib-box">
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-youduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">#icon-youduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xiaduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">下对齐</div>
|
||||
<div class="code-name">#icon-xiaduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shuipingjuzhongduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">水平居中对齐</div>
|
||||
<div class="code-name">#icon-shuipingjuzhongduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zuoduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">#icon-zuoduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-card-text-center"></use>
|
||||
</svg>
|
||||
<div class="name">居中对齐</div>
|
||||
<div class="code-name">#icon-card-text-center</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-shangduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">上对齐</div>
|
||||
<div class="code-name">#icon-shangduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zuoduiqi2"></use>
|
||||
</svg>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">#icon-zuoduiqi2</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-youduiqi1"></use>
|
||||
</svg>
|
||||
<div class="name">右对齐</div>
|
||||
<div class="code-name">#icon-youduiqi1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-zuoduiqi1"></use>
|
||||
</svg>
|
||||
<div class="name">左对齐</div>
|
||||
<div class="code-name">#icon-zuoduiqi1</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-Undo"></use>
|
||||
</svg>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">#icon-Undo</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-chuizhijuzhongduiqi"></use>
|
||||
</svg>
|
||||
<div class="name">垂直居中对齐</div>
|
||||
<div class="code-name">#icon-chuizhijuzhongduiqi</div>
|
||||
</li>
|
||||
|
||||
<li class="dib">
|
||||
<svg class="icon svg-icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-ic_undo"></use>
|
||||
</svg>
|
||||
<div class="name">undo</div>
|
||||
<div class="code-name">#icon-ic_undo</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<div class="article markdown">
|
||||
<h2 id="symbol-">Symbol 引用</h2>
|
||||
<hr>
|
||||
|
||||
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
|
||||
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
|
||||
<ul>
|
||||
<li>支持多色图标了,不再受单色限制。</li>
|
||||
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
|
||||
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
|
||||
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
|
||||
</ul>
|
||||
<p>使用步骤如下:</p>
|
||||
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
|
||||
<pre><code class="language-html"><script src="./iconfont.js"></script>
|
||||
</code></pre>
|
||||
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
|
||||
<pre><code class="language-html"><style>
|
||||
.icon {
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: currentColor;
|
||||
overflow: hidden;
|
||||
}
|
||||
</style>
|
||||
</code></pre>
|
||||
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
|
||||
<pre><code class="language-html"><svg class="icon" aria-hidden="true">
|
||||
<use xlink:href="#icon-xxx"></use>
|
||||
</svg>
|
||||
</code></pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
$('.tab-container .content:first').show()
|
||||
|
||||
$('#tabs li').click(function (e) {
|
||||
var tabContent = $('.tab-container .content')
|
||||
var index = $(this).index()
|
||||
|
||||
if ($(this).hasClass('active')) {
|
||||
return
|
||||
} else {
|
||||
$('#tabs li').removeClass('active')
|
||||
$(this).addClass('active')
|
||||
|
||||
tabContent.hide().eq(index).fadeIn()
|
||||
}
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
65
src/components/poster-editor/icon/iconfont.css
Normal file
65
src/components/poster-editor/icon/iconfont.css
Normal file
@ -0,0 +1,65 @@
|
||||
@font-face {font-family: "iconfont";
|
||||
src: url('iconfont.eot?t=1578558136402'); /* IE9 */
|
||||
src: url('iconfont.eot?t=1578558136402#iefix') format('embedded-opentype'), /* IE6-IE8 */
|
||||
url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAAAYYAAsAAAAADMwAAAXLAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCEUgqKGId2ATYCJAM0CxwABCAFhG0HgUobxAojEaaMjp/sLxLMk0wHLVmncXb21PWwcny3xMGD4l8oJema4IHozH+zMxEOmwJO8uFUzpXOpgByNXhX++8HkAQIyENwg7lbAW7qrO0j3q7pt+DY9WnHNeDIsULKy3Kpdy10UV+stfpu03VfDAuFGt/EENXQIBMShCLi8eciHiqEQsmdxbya7IRiUma8ej8E4CS/skS1GvWaYEfBDxOA6Nkt3hF7JIDKYQvsbitxrnpXzMaCXZuiZQGznN8X3xDMDhoWCX9Q1KjehYiJOUYmkgnayEwy24sBt4tAAmWBArGXTGxHem1lSZwfZBQJIMBPYjUxpekzg2Zds4GZYbY2+5pjkkkY3tssOhdPCAICnxOb20Ff/+bZcWBBw4oNNwIXCoQVdLhh2epI0EQBEkwVnGB6VbCD6VPBAWZQBQuYdVXQwGygghXMqAo2MDMM/ThbgwoCzL4quMAcYzrRDo91Tm5QAaIriBNwVxdHWaGwYDWKLWhYEArRbnPg8kMgn///AtcIl9XZ3aO5O+IVfbyabwT+BmhO2UzVprlbeToKF62kS2smPVoHH95mmk+2dynnAM0rewkv7epZ4/JWsqNw0zxt2onEcWfq1JOZp+K+AwHG62+VQnHC/oMOTz7G3dJepNClzRZDOS2d+xnZ1GlzxdzS06UPdKvTR7BkSzHfpm15xNKtxb3rN7eIolKGC5G51emfujkmbTn/PJdvGmpof0hscaVNK24fEYr4ptXFa/q/Hpwty+qbNyC17/pgsTTQQi3uP24mFi1dn95lfkKPLdgkxL1XhUgs0MWiRaL/1PlgxMMnue49anr/dWsXzs4e3M0Xw9VuMt+2h+gSkxE8eNVSPPg6FModCoeyspay1Ihlkx0MGiGD9+lQkYo3diV+TMya9fL0yx8qFIwU5PNc8XA8tGRpeGlWEIO9MbIJJkNGiCVZoaWha0HfF0w20DNnPQNjTK6kNRa+eSibWim1CxtV44VrFxpSdXCuoi0wqhqFWrx2MVoVzSg0uOpgMpiWZLUvHPcVbg83H0K7w+Eq4Z5311jW9OzpvOVUoQ/6f8DFgnfy3Z6KPXo6bjnCaid9X4OgEj0ZOaYcVCR+7Yv3rqgjUT2oq+2PpGc1sgSOWSpZ6NNxj0Al/XigiiiiZ+UE8+lVXgRpDSulXwtcDByTldSjF6NXP6gkRc9Hjyo8OXBefxK0dS29kn41Oj9yVS+X1ecVibq2irFA6YjxhSJEcMa+f+PJ5+ObFs93PwsOTI09azI+p9PZ1NSHA4VtcQc6LLb7q9qW2LbvCO1I22FfbLtl//3nXV+1kWMJbBz16ajfP/nzU/Fav32086NRDuNf48NPhr8ezptJf745+OaQc6A7O7GweHnLapXTZcyfQNKUcZkbQCa0m1F8mB3XskVjylOZYK/Rvmt5IXvJQhhPqLlUIZmp5sDefyghcwHIH+OvvNR5mamz/obdi1Y8L3wf/zZcCoDHn8jDaGh/fCZgR4PSRn00RT2B5F/q6d3Mgx71nkUtfxKg5yyfHXWeaLMdDxF0RYsGOEUGkpBJ9bwfl156IAjs6AFo2MgHEjtF0QpfFiy4CIMVO5+BkzJqXuwiTX8kQjmA0iYwEPjtAg0vp0Dil41W+GdgIeA7sOJPwgOX/nq65pWX6PJVsFJsoN4DGZ6sDOulO/gL2zBUEjY7/D8UT2XI4jTe+YkTShsn+E2bq1qwwiN8sIfhMDDMwh0ajZ3qvE4SSyEUGx4Xl14JVFGoAWp7gBhsYq311WXi57+gVjCoSM6sn6Z/kPBo8yATS0ugPx1TqVnHMtLbaOUUWyxgbwPZCHxgDA2ijYGZ3quDDBVzNVpmawmuZss64v5d4zHfADhx5DyFrKiabpiW7bheOp9DE+ifzndUudFn7+z6M01tFw6OjTncQ+Cd700lTaS408jgTnTIpXdVAcJFOL9o0ZMlR2THv1PDT6bhNQdHl5YzMkWg/YsF') format('woff2'),
|
||||
url('iconfont.woff?t=1578558136402') format('woff'),
|
||||
url('iconfont.ttf?t=1578558136402') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+ */
|
||||
url('iconfont.svg?t=1578558136402#iconfont') format('svg'); /* iOS 4.1- */
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-youduiqi:before {
|
||||
content: "\e65b";
|
||||
}
|
||||
|
||||
.icon-xiaduiqi:before {
|
||||
content: "\e64b";
|
||||
}
|
||||
|
||||
.icon-shuipingjuzhongduiqi:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-zuoduiqi:before {
|
||||
content: "\e67f";
|
||||
}
|
||||
|
||||
.icon-card-text-center:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.icon-shangduiqi:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.icon-zuoduiqi2:before {
|
||||
content: "\e650";
|
||||
}
|
||||
|
||||
.icon-youduiqi1:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.icon-zuoduiqi1:before {
|
||||
content: "\e60f";
|
||||
}
|
||||
|
||||
.icon-Undo:before {
|
||||
content: "\e603";
|
||||
}
|
||||
|
||||
.icon-chuizhijuzhongduiqi:before {
|
||||
content: "\e670";
|
||||
}
|
||||
|
||||
.icon-ic_undo:before {
|
||||
content: "\e651";
|
||||
}
|
||||
|
||||
BIN
src/components/poster-editor/icon/iconfont.eot
Normal file
BIN
src/components/poster-editor/icon/iconfont.eot
Normal file
Binary file not shown.
2
src/components/poster-editor/icon/iconfont.js
Normal file
2
src/components/poster-editor/icon/iconfont.js
Normal file
File diff suppressed because one or more lines are too long
93
src/components/poster-editor/icon/iconfont.json
Normal file
93
src/components/poster-editor/icon/iconfont.json
Normal file
@ -0,0 +1,93 @@
|
||||
{
|
||||
"id": "1577785",
|
||||
"name": "classcard",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "657594",
|
||||
"name": "右对齐",
|
||||
"font_class": "youduiqi",
|
||||
"unicode": "e65b",
|
||||
"unicode_decimal": 58971
|
||||
},
|
||||
{
|
||||
"icon_id": "2404492",
|
||||
"name": "下对齐",
|
||||
"font_class": "xiaduiqi",
|
||||
"unicode": "e64b",
|
||||
"unicode_decimal": 58955
|
||||
},
|
||||
{
|
||||
"icon_id": "2684435",
|
||||
"name": "水平居中对齐",
|
||||
"font_class": "shuipingjuzhongduiqi",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "2791240",
|
||||
"name": "左对齐",
|
||||
"font_class": "zuoduiqi",
|
||||
"unicode": "e67f",
|
||||
"unicode_decimal": 59007
|
||||
},
|
||||
{
|
||||
"icon_id": "4297547",
|
||||
"name": "居中对齐",
|
||||
"font_class": "card-text-center",
|
||||
"unicode": "e632",
|
||||
"unicode_decimal": 58930
|
||||
},
|
||||
{
|
||||
"icon_id": "4875059",
|
||||
"name": "上对齐",
|
||||
"font_class": "shangduiqi",
|
||||
"unicode": "e64e",
|
||||
"unicode_decimal": 58958
|
||||
},
|
||||
{
|
||||
"icon_id": "4875066",
|
||||
"name": "左对齐",
|
||||
"font_class": "zuoduiqi2",
|
||||
"unicode": "e650",
|
||||
"unicode_decimal": 58960
|
||||
},
|
||||
{
|
||||
"icon_id": "5003035",
|
||||
"name": "右对齐",
|
||||
"font_class": "youduiqi1",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "5003037",
|
||||
"name": "左对齐",
|
||||
"font_class": "zuoduiqi1",
|
||||
"unicode": "e60f",
|
||||
"unicode_decimal": 58895
|
||||
},
|
||||
{
|
||||
"icon_id": "8074641",
|
||||
"name": "undo",
|
||||
"font_class": "Undo",
|
||||
"unicode": "e603",
|
||||
"unicode_decimal": 58883
|
||||
},
|
||||
{
|
||||
"icon_id": "8229624",
|
||||
"name": "垂直居中对齐",
|
||||
"font_class": "chuizhijuzhongduiqi",
|
||||
"unicode": "e670",
|
||||
"unicode_decimal": 58992
|
||||
},
|
||||
{
|
||||
"icon_id": "8756570",
|
||||
"name": "undo",
|
||||
"font_class": "ic_undo",
|
||||
"unicode": "e651",
|
||||
"unicode_decimal": 58961
|
||||
}
|
||||
]
|
||||
}
|
||||
62
src/components/poster-editor/icon/iconfont.svg
Normal file
62
src/components/poster-editor/icon/iconfont.svg
Normal file
@ -0,0 +1,62 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<!--
|
||||
2013-9-30: Created.
|
||||
-->
|
||||
<svg>
|
||||
<metadata>
|
||||
Created by iconfont
|
||||
</metadata>
|
||||
<defs>
|
||||
|
||||
<font id="iconfont" horiz-adv-x="1024" >
|
||||
<font-face
|
||||
font-family="iconfont"
|
||||
font-weight="500"
|
||||
font-stretch="normal"
|
||||
units-per-em="1024"
|
||||
ascent="896"
|
||||
descent="-128"
|
||||
/>
|
||||
<missing-glyph />
|
||||
|
||||
<glyph glyph-name="youduiqi" unicode="" d="M819.2 793.6l51.2 0 0-819.2-51.2 0 0 819.2ZM358.4 691.2l409.6 0 0-256-409.6 0 0 256ZM716.8 332.8l0-204.8L204.8 128l0 204.8L716.8 332.8M768 384 153.6 384l0-307.2 614.4 0L768 384 768 384z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="xiaduiqi" unicode="" d="M896 96v-48H128v48h768zM848 528V144h-240V528h240zM464 672v-480H272v480h192m48 48H224V144H512V720z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="shuipingjuzhongduiqi" unicode="" d="M486.40000000000015-25.600000000000136l-5.684341886080802e-14 102.4 51.2 2.842170943040401e-14 5.684341886080802e-14-102.4-51.2-2.842170943040401e-14ZM486.40000000000003 383.9999999999999l-2.2737367544323206e-13 409.6 51.2 2.842170943040401e-14 2.2737367544323206e-13-409.6-51.2-2.842170943040401e-14ZM332.79999999999995 435.19999999999993l-1.1368683772161603e-13 256 358.4 1.7053025658242404e-13 1.1368683772161603e-13-256-358.4-1.7053025658242404e-13ZM256.00000000000006 332.79999999999995l512 2.2737367544323206e-13L768 128.0000000000001 256.00000000000006 128 256.00000000000006 332.79999999999995M204.79999999999995 384L204.80000000000024 76.79999999999984l614.3999999999999 2.2737367544323206e-13-1.1368683772161603e-13 307.19999999999993L204.79999999999995 384 204.79999999999995 384z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="zuoduiqi" unicode="" d="M153.6 716.8h51.2v-665.6h-51.2zM870.4 358.4v-307.2H256V358.4h614.4z m-51.2-256V307.2H307.2v-204.8h512zM281.6 691.2h358.4v-256H281.6zM665.6 716.8v-307.2H256V716.8h409.6z m-51.2-256V665.6H307.2v-204.8h307.2z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="card-text-center" unicode="" d="M73.142857 18.285713999999984h1097.142857c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H73.142857c-43.885714 0-73.142857 29.257143-73.142857 73.142857s29.257143 73.142857 73.142857 73.142857zM321.828571 310.85714299999995h599.771429c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H321.828571c-43.885714 0-73.142857 29.257143-73.142857 73.142857s29.257143 73.142857 73.142857 73.142857zM197.485714 603.4285709999999h848.457143c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H197.485714c-43.885714 0-73.142857 29.257143-73.142857 73.142857s29.257143 73.142857 73.142857 73.142857zM446.171429 896h351.085714c43.885714 0 73.142857-29.257143 73.142857-73.142857s-29.257143-73.142857-73.142857-73.142857H446.171429c-43.885714 0-73.142857 29.257143-73.142858 73.142857s29.257143 73.142857 73.142858 73.142857z" horiz-adv-x="1243" />
|
||||
|
||||
|
||||
<glyph glyph-name="shangduiqi" unicode="" d="M95.001 696.75v52.125H929v-52.125H95.001z m469.124-469.125v417H824.75v-416.999H564.125zM459.875 592.5v-521.25h-208.5V592.5h208.5M512 644.625H199.23v-625.499H512V644.625z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="zuoduiqi2" unicode="" d="M199.25-32.999h-52.125V800.999h52.125v-833.998z m469.125 469.124h-417V696.75h416.999v-260.625zM303.5 331.875h521.25v-208.5H303.5v208.5M251.375 384v-312.77h625.499V384H251.375z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="youduiqi1" unicode="" d="M347.46496 845.91744v-49.81248c0-27.44704 22.29376-49.81376 50.0864-49.81376h701.9904c27.6608 0 50.0864 22.30144 50.0864 49.81376V845.91744c0 27.51232-22.4256 49.81376-50.0864 49.81376h-701.696c-27.70944 0-50.21824-22.25536-50.3808-49.81376zM116.3712 554.67648v-49.81248c0-27.34848 22.29248-49.81376 50.0864-49.81376h952.51712c27.6608 0 50.0864 22.30144 50.0864 49.81376V554.6752c0 27.51232-22.4256 49.81376-50.0864 49.81376H166.4576a49.93152 49.93152 0 0 1-35.50336-14.5024 49.3824 49.3824 0 0 1-14.58304-35.31136z m339.24864-241.42464c-27.73248 0.05376-50.272-22.2336-50.3808-49.81376v-49.81376c0-27.44576 22.4896-49.81376 50.18496-49.81376h651.80544c27.6544 0.05376 50.07616 22.30912 50.18368 49.81376V263.4368c0 27.34848-22.4896 49.81376-50.18368 49.81376h-651.6096zM51.20384 22.01088C23.47008 22.06464 0.92928-0.22144 0.82176-27.8016v-49.81248c0-27.44704 22.4896-49.81376 50.18368-49.81376h1052.88576c27.66208 0 50.0864 22.30144 50.0864 49.81376v49.81248c0 27.51232-22.42432 49.81376-50.0864 49.81376H51.20256z" horiz-adv-x="1169" />
|
||||
|
||||
|
||||
<glyph glyph-name="zuoduiqi1" unicode="" d="M0.832 847.08608v-48.65024c0-26.80576 22.5728-48.6528 50.7136-48.6528h710.76352c28.00768 0 50.71232 21.78304 50.71232 48.6528V847.08608c0 26.86976-22.70464 48.6528-50.71232 48.6528H51.84256C23.78752 895.7376 0.9984 874.00064 0.83328 847.0848z m0.29952-292.288v-48.65152c0-26.70976 22.57152-48.65152 50.71104-48.65152H1016.2688c28.0064 0 50.71232 21.78176 50.71232 48.6528v48.65024c0 26.86848-22.70464 48.65152-50.7136 48.65152H51.84384c-13.47328 0.0768-26.4192-5.02528-35.94624-14.16576C6.368 580.14464 1.0496 567.7248 1.13152 554.79808z m50.71104-243.44704C23.76448 311.40352 0.94208 289.6384 0.83328 262.69952v-48.65024c0-26.80576 22.7712-48.6528 50.81088-48.6528h659.95264c28.00128 0.05376 50.70208 21.78944 50.81216 48.6528v48.65024c0 26.71104-22.7712 48.6528-50.81216 48.6528H51.84256z m0-292.384C23.76448 19.01952 0.94208-2.74688 0.83328-29.68448v-48.65024c0-26.80576 22.7712-48.6528 50.81088-48.6528h1066.048c28.0064 0 50.71104 21.78304 50.71104 48.6528v48.65024c0 26.86976-22.70464 48.65152-50.71104 48.65152H51.84256z" horiz-adv-x="1168" />
|
||||
|
||||
|
||||
<glyph glyph-name="Undo" unicode="" d="M1023.99732 9.762c-4.13 223.3-169.472 551.151-511.966 561.698V759.467a17.067 17.067 0 1 1-26.317 14.37L7.84832 466.637a17.033 17.033 0 0 1-0.683-28.228L485.03132 97.075A17.135 17.135 0 0 1 512.03132 110.933V298.36c252.28-9.898 475.648-235.28 477.833-289.006l0.034-1.093a17.1 17.1 0 0 1 17.066-16.793h0.137a17.067 17.067 0 0 1 16.93 17.066l-0.034 1.23zM494.96432 332.8a17.067 17.067 0 0 1-17.066-17.067v-171.622L47.51032 451.516 477.89832 728.2v-173.534a17.067 17.067 0 0 1 17.066-17.067c279.552 0 447.898-238.763 486.469-447.317C895.48532 193.673 692.63032 332.8 494.96432 332.8z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="chuizhijuzhongduiqi" unicode="" d="M0.512 422.7072h125.8496v-61.44H0.512v61.44zM504.0128 422.7072h503.5008v-61.44H504.0128v61.44zM566.9376 607.0272h314.6752v-430.08h-314.6752zM441.0368 699.1872v-614.4H189.2864v614.4h251.7504z m62.976 61.44H126.3616v-737.28h377.6512v737.28z" horiz-adv-x="1024" />
|
||||
|
||||
|
||||
<glyph glyph-name="ic_undo" unicode="" d="M484.308604 648.193618v247.831671L0 456.655142 484.308604 17.209128V282.945372c134.562481 0 550.186308 0 729.560802-410.920083v45.646547c0 182.636768-179.399783 769.390892-729.560802 730.521782z" horiz-adv-x="1213" />
|
||||
|
||||
|
||||
|
||||
|
||||
</font>
|
||||
</defs></svg>
|
||||
|
After Width: | Height: | Size: 7.0 KiB |
BIN
src/components/poster-editor/icon/iconfont.ttf
Normal file
BIN
src/components/poster-editor/icon/iconfont.ttf
Normal file
Binary file not shown.
BIN
src/components/poster-editor/icon/iconfont.woff
Normal file
BIN
src/components/poster-editor/icon/iconfont.woff
Normal file
Binary file not shown.
BIN
src/components/poster-editor/icon/iconfont.woff2
Normal file
BIN
src/components/poster-editor/icon/iconfont.woff2
Normal file
Binary file not shown.
35
src/components/poster-editor/icon/index.vue
Normal file
35
src/components/poster-editor/icon/index.vue
Normal file
@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<svg
|
||||
:class="{active: active}"
|
||||
class="icon el-icon cursor-p" aria-hidden="true">
|
||||
<use :xlink:href="'#icon-' + icon"></use>
|
||||
</svg>
|
||||
</template>
|
||||
<script>
|
||||
import './iconfont.js'
|
||||
export default {
|
||||
props: {
|
||||
icon: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
active: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.icon{
|
||||
font-size: 18px;
|
||||
width: 1em;
|
||||
height: 1em;
|
||||
vertical-align: -0.15em;
|
||||
fill: rgba(0,0,0,0.5);
|
||||
overflow: hidden;
|
||||
&.active{
|
||||
fill: rgba(0,0,0,1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
39
src/components/poster-editor/imgWidget.vue
Normal file
39
src/components/poster-editor/imgWidget.vue
Normal file
@ -0,0 +1,39 @@
|
||||
<template>
|
||||
<img
|
||||
v-if="value"
|
||||
:style="{
|
||||
transform: `scale(${needScale ? dZoom : 1})`
|
||||
}"
|
||||
class="img-widget-ct"
|
||||
:src="value"
|
||||
/>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 解决chrome字体无法更小的问题
|
||||
needScale: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
},
|
||||
created() {
|
||||
console.log(this.value);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.img-widget-ct {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
11
src/components/poster-editor/index.js
Normal file
11
src/components/poster-editor/index.js
Normal file
@ -0,0 +1,11 @@
|
||||
import 'element-ui/lib/theme-chalk/index.css'
|
||||
import Vue from 'vue'
|
||||
import ElementUI from 'element-ui'
|
||||
import './assets/reset.css'
|
||||
import './assets/common.scss'
|
||||
import PosterEditor from './index.vue'
|
||||
Vue.use(ElementUI)
|
||||
PosterEditor.install = function (Vue) {
|
||||
Vue.component(PosterEditor.name, PosterEditor)
|
||||
}
|
||||
export default PosterEditor
|
||||
750
src/components/poster-editor/index.vue
Normal file
750
src/components/poster-editor/index.vue
Normal file
@ -0,0 +1,750 @@
|
||||
<template>
|
||||
<div class="poster-editor-comp" :id="editorUid">
|
||||
<div class="main-pane" :style="{ minHeight: 720 * dZoom + 58 + 'px' }">
|
||||
<div class="header-ct">
|
||||
<div class="left">
|
||||
<el-tooltip effect="dark" content="撤销" placement="top-start">
|
||||
<el-button
|
||||
:disabled="!canClickPrev"
|
||||
@click="getHistory(-1)"
|
||||
type="text"
|
||||
>
|
||||
<Icon :active="canClickPrev" class="m-r-16" icon="Undo"></Icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
<el-tooltip effect="dark" content="前进" placement="top-start">
|
||||
<el-button
|
||||
:disabled="!canClickNext"
|
||||
@click="getHistory(1)"
|
||||
type="text"
|
||||
>
|
||||
<Icon :active="canClickNext" class="mirror" icon="Undo"></Icon>
|
||||
</el-button>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
<div class="center">
|
||||
轻点元素开始编辑
|
||||
</div>
|
||||
<div class="right">
|
||||
<el-button @click="submitImg">提交</el-button>
|
||||
</div>
|
||||
</div>
|
||||
<draggable
|
||||
ref="posterContent"
|
||||
v-bind="getOptions()"
|
||||
@end="isDragging = false"
|
||||
@start="isDragging = true"
|
||||
:style="{
|
||||
width: dZoom * page.width + 'px',
|
||||
height: dZoom * page.height + 'px',
|
||||
backgroundColor: value.background.color
|
||||
}"
|
||||
class="poster-content m-b-16"
|
||||
tag="div"
|
||||
>
|
||||
<img class="bg-puppet" :src="value && value.background.img" />
|
||||
<widget
|
||||
:class="{ disabled: !item.active }"
|
||||
@click.native="handleActiveWidget(item)"
|
||||
@dragstart.native="dragStart($event, item)"
|
||||
@dragend.native="dragEnd($event, item, index)"
|
||||
@modify="pushHistory"
|
||||
:widget-data="item"
|
||||
:order="index"
|
||||
:page="page"
|
||||
:d-zoom="dZoom"
|
||||
:key="item.uuid"
|
||||
v-for="(item, index) in widgetList"
|
||||
>
|
||||
</widget>
|
||||
</draggable>
|
||||
</div>
|
||||
<div class="right-pane">
|
||||
<el-tabs>
|
||||
<el-tab-pane label="当前组件">
|
||||
<div v-if="activeWidget" class="style-setting-ct">
|
||||
<el-collapse v-model="activeNames">
|
||||
<el-collapse-item
|
||||
v-if="activeWidget.setDataRange.content.show"
|
||||
title="文本内容"
|
||||
name="1"
|
||||
>
|
||||
<div class="style-config m-r-16">
|
||||
<el-input
|
||||
v-model="activeWidget.content"
|
||||
type="textarea"
|
||||
></el-input>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
v-if="activeWidget.setDataRange.box.show"
|
||||
title="组件样式"
|
||||
name="2"
|
||||
>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
X
|
||||
</span>
|
||||
<el-input-number
|
||||
:precision="0"
|
||||
v-model="activeWidget.style.left"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
Y
|
||||
</span>
|
||||
<el-input-number
|
||||
:precision="0"
|
||||
v-model="activeWidget.style.top"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
宽度
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.width"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
高度
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.height"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<template
|
||||
v-if="activeWidget.setDataRange.box.detail.background"
|
||||
>
|
||||
<span class="label">
|
||||
背景色
|
||||
</span>
|
||||
<el-color-picker
|
||||
v-model="activeWidget.style.background"
|
||||
class="w-60"
|
||||
show-alpha
|
||||
size="mini"
|
||||
></el-color-picker>
|
||||
</template>
|
||||
<span class="label">
|
||||
层级
|
||||
</span>
|
||||
<el-input-number
|
||||
disabled
|
||||
:value="activeWidget.style.zIndex"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<!-- <span class="label">
|
||||
对齐
|
||||
</span>
|
||||
{{activeWidget.style.textAlign}}-->
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
<el-collapse-item
|
||||
v-if="activeWidget.setDataRange.innerStyle.show"
|
||||
title="字体样式"
|
||||
name="3"
|
||||
>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
大小
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.fontSize"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
行高
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.lineHeight"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
字距
|
||||
</span>
|
||||
<el-input-number
|
||||
v-model="activeWidget.style.letterSpacing"
|
||||
class="w-60"
|
||||
:controls="false"
|
||||
size="mini"
|
||||
></el-input-number>
|
||||
<span class="label">
|
||||
颜色
|
||||
</span>
|
||||
<el-color-picker
|
||||
v-model="activeWidget.style.color"
|
||||
class="w-60"
|
||||
show-alpha
|
||||
size="mini"
|
||||
></el-color-picker>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<!--<span class="label">
|
||||
对齐
|
||||
</span>
|
||||
<el-input-number class="w-60" :controls="false" size="mini"></el-input-number>-->
|
||||
<!-- <span class="label">
|
||||
字体
|
||||
</span>
|
||||
<el-input-number class="w-60" :controls="false" size="mini"></el-input-number>-->
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">
|
||||
对齐
|
||||
</span>
|
||||
<align-select
|
||||
v-model="activeWidget.style.align"
|
||||
></align-select>
|
||||
</div>
|
||||
<div class="style-config">
|
||||
<span class="label">其他</span>
|
||||
<span
|
||||
:class="{ active: activeWidget.style.isBold }"
|
||||
@click="handleOtherClick('isBold')"
|
||||
class="cursor-p other"
|
||||
><b>B</b></span
|
||||
>
|
||||
<span
|
||||
:class="{ active: activeWidget.style.isOblique }"
|
||||
@click="handleOtherClick('isOblique')"
|
||||
class="m-l-8 cursor-p other"
|
||||
><i>I</i></span
|
||||
>
|
||||
<span></span>
|
||||
</div>
|
||||
</el-collapse-item>
|
||||
</el-collapse>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import _ from 'lodash';
|
||||
import draggable from 'vuedraggable';
|
||||
import uuid from 'uuid';
|
||||
import html2canvas from 'html2canvas';
|
||||
import { Loading } from 'element-ui';
|
||||
import alignSelect from './alignSelect';
|
||||
import widget from './widget';
|
||||
import blankDialog from '@/components/blankDialog';
|
||||
import imageSelection from '@/components/imageSelection';
|
||||
import { setDataRangeMap, defaultDataMap } from './widgetInitDataMap';
|
||||
import Icon from './icon/index';
|
||||
import { qrUpdate } from '@/api/index';
|
||||
import VueQr from 'vue-qr';
|
||||
|
||||
let zIndex = 0;
|
||||
|
||||
export default {
|
||||
name: 'posterEditor',
|
||||
props: {
|
||||
order: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
value: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
page: {
|
||||
type: Object,
|
||||
default() {
|
||||
return {
|
||||
width: 375,
|
||||
height: 620
|
||||
};
|
||||
}
|
||||
}
|
||||
},
|
||||
components: {
|
||||
widget,
|
||||
draggable,
|
||||
alignSelect,
|
||||
blankDialog,
|
||||
imageSelection,
|
||||
Icon,
|
||||
VueQr
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
editorUid: 'uid' + uuid(),
|
||||
dZoom: 0.52,
|
||||
isDragging: false,
|
||||
activeNames: ['1', '2', '3', '4', '5', '6'],
|
||||
widgetList: [],
|
||||
dragStartPosition: null,
|
||||
widgetDragStart: null, // 当前拖的是哪个文本图层 便于后期交互值
|
||||
handleResizeFn: null,
|
||||
handleActiveWidgetChange: null,
|
||||
showImageSelection: false,
|
||||
showImageSelectionWidget: false,
|
||||
widgetHistory: [], // 记录历史记录变动的
|
||||
historyIndex: -1
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
submitImg() {
|
||||
// const
|
||||
console.log(this.id);
|
||||
// qrUpdate(this.value).then(res => {
|
||||
// console.log(res);
|
||||
// });
|
||||
},
|
||||
beforeAddImgWidget(imgObj) {
|
||||
this.addWidget('img', imgObj.path);
|
||||
this.showImageSelectionWidget = false;
|
||||
},
|
||||
addWidget(type, path) {
|
||||
zIndex++;
|
||||
const typeLabelMap = {
|
||||
text: '文本',
|
||||
img: '图片'
|
||||
};
|
||||
const item = {
|
||||
label: typeLabelMap[type],
|
||||
type,
|
||||
content: type === 'text' ? '文本' : path,
|
||||
uuid: uuid(),
|
||||
active: false,
|
||||
setDataRange: setDataRangeMap[type],
|
||||
style: {
|
||||
...defaultDataMap[type],
|
||||
zIndex
|
||||
}
|
||||
};
|
||||
this.widgetList.push(item);
|
||||
this.$nextTick(() => {
|
||||
this.handleActiveWidget(item);
|
||||
});
|
||||
console.log(this.value);
|
||||
this.$emit('input', {
|
||||
background: this.value.background,
|
||||
widgetList: this.widgetList
|
||||
});
|
||||
this.pushHistory('add', item, this.widgetList.length - 1);
|
||||
},
|
||||
handleActiveWidget(item) {
|
||||
this.widgetList.forEach(w => {
|
||||
if (item.uuid === w.uuid) {
|
||||
w.active = !item.active;
|
||||
} else {
|
||||
w.active = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
getOptions() {
|
||||
return {
|
||||
filter: '.disabled'
|
||||
};
|
||||
},
|
||||
dragStart(e) {
|
||||
this.dragStartPosition = {
|
||||
pageX: e.pageX,
|
||||
pageY: e.pageY
|
||||
};
|
||||
},
|
||||
dragEnd(e, item, index) {
|
||||
item.history = {
|
||||
content: item.content,
|
||||
style: _.cloneDeep(item.style)
|
||||
};
|
||||
let target = item.style;
|
||||
let left = (e.pageX - this.dragStartPosition.pageX) / this.dZoom;
|
||||
let top = (e.pageY - this.dragStartPosition.pageY) / this.dZoom;
|
||||
target.left = Math.max(
|
||||
Math.min(left + target.left, this.page.width - target.width),
|
||||
0
|
||||
);
|
||||
target.top = Math.max(
|
||||
Math.min(top + target.top, this.page.height - target.height),
|
||||
0
|
||||
);
|
||||
this.pushHistory('modify', item, index);
|
||||
},
|
||||
deleteWidget(item) {
|
||||
let index = this.widgetList.findIndex(v => v.uuid === item.uuid);
|
||||
if (index > -1) {
|
||||
this.$confirm(
|
||||
`此操作将永久删除该${item.label}图层, 是否继续?`,
|
||||
'警告',
|
||||
{
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}
|
||||
).then(() => {
|
||||
this.widgetList.splice(index, 1);
|
||||
this.pushHistory('delete', item, index);
|
||||
});
|
||||
}
|
||||
},
|
||||
handleOtherClick(key) {
|
||||
this.activeWidget.style[key] = !this.activeWidget.style[key];
|
||||
},
|
||||
dragZIndexStart(item) {
|
||||
this.widgetDragStart = item;
|
||||
},
|
||||
dragZIndexEnd(item) {
|
||||
let tmp = item.style.zIndex;
|
||||
item.style.zIndex = this.widgetDragStart.style.zIndex;
|
||||
this.widgetDragStart.style.zIndex = tmp;
|
||||
this.widgetDragStart = null;
|
||||
},
|
||||
getDZoom() {
|
||||
// const currentEditorBox = document.querySelector(`#${this.editorUid}`).getBoundingClientRect()
|
||||
// fix v-show隐藏元素宽度太小的问题
|
||||
const currentEditorBox = document
|
||||
.querySelector(`.poster-editor-comp`)
|
||||
.getBoundingClientRect();
|
||||
let width = currentEditorBox.width - 32 - 295;
|
||||
if (width >= 480) {
|
||||
this.dZoom = Number((width / 960).toFixed(2));
|
||||
}
|
||||
if (width >= 960) {
|
||||
this.dZoom = 1;
|
||||
}
|
||||
},
|
||||
// 上传当前poster截图
|
||||
uploadCurrentPosterShot(cb) {
|
||||
let instance = Loading.service({
|
||||
text: '保存中,请稍候!',
|
||||
background: 'rgba(0, 0, 0, 0.5)'
|
||||
});
|
||||
console.time('1');
|
||||
this.widgetList.forEach(w => {
|
||||
w.active = false;
|
||||
});
|
||||
return new Promise(resolve => {
|
||||
// 激活当前编辑器 方便截图
|
||||
this.$emit('active');
|
||||
this.$nextTick(() => {
|
||||
html2canvas(
|
||||
document
|
||||
.querySelector(`#${this.editorUid}`)
|
||||
.querySelector('.poster-content'),
|
||||
{
|
||||
useCORS: true,
|
||||
// 编辑器缩放保持 960 * 720 的尺寸
|
||||
scale: 1 / this.dZoom
|
||||
}
|
||||
).then(async canvas => {
|
||||
console.timeEnd('1');
|
||||
console.time('2');
|
||||
let file = canvas
|
||||
.toDataURL('image/jpeg', 1)
|
||||
.replace('data:image/jpeg;base64,', '');
|
||||
// base64的图片
|
||||
console.timeEnd('2');
|
||||
console.time('3');
|
||||
if (cb) {
|
||||
await cb(file, resolve);
|
||||
console.timeEnd('3');
|
||||
instance.close();
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
changeBackground(imgUrl) {
|
||||
console.log((this.value.background.img = imgUrl));
|
||||
// this.value.background = imgUrl;
|
||||
// this.value.material_uid = imgObj.uid;
|
||||
// this.value.materialType = imgObj.expandType;
|
||||
this.showImageSelection = false;
|
||||
},
|
||||
changeTextColor(color) {
|
||||
this.value.background.color = color;
|
||||
console.log(this.value.background);
|
||||
},
|
||||
pushHistory(type, item, index) {
|
||||
const obj = {
|
||||
uuid: item.uuid,
|
||||
item,
|
||||
index,
|
||||
type
|
||||
};
|
||||
if (this.canClickNext) {
|
||||
// 已经执行过撤销的操作了 再添加历史记录把后面的删除掉
|
||||
this.widgetHistory.length = this.historyIndex + 1;
|
||||
}
|
||||
this.widgetHistory.push(obj);
|
||||
this.historyIndex = this.widgetHistory.length - 1;
|
||||
/* switch (type) {
|
||||
case 'add':
|
||||
obj.type = 'add'
|
||||
|
||||
this.historyIndex = this.widgetHistory.length - 1
|
||||
break
|
||||
case 'delete':
|
||||
obj.type = 'delete'
|
||||
this.widgetHistory.push(obj)
|
||||
this.historyIndex = this.widgetHistory.length
|
||||
break
|
||||
case 'modify':
|
||||
break
|
||||
} */
|
||||
},
|
||||
getHistory(num) {
|
||||
if ((num < 0 && this.canClickPrev) || (num > 0 && this.canClickNext)) {
|
||||
if (num > 0) this.historyIndex += num;
|
||||
let historyObj = this.widgetHistory[this.historyIndex];
|
||||
switch (historyObj.type) {
|
||||
case 'add':
|
||||
// 撤回操作
|
||||
if (num < 0) {
|
||||
let index = this.widgetList.findIndex(
|
||||
v => v.uuid === historyObj.uuid
|
||||
);
|
||||
if (index > -1) {
|
||||
this.widgetList.splice(index, 1);
|
||||
}
|
||||
} else {
|
||||
this.widgetList.push(historyObj.item);
|
||||
}
|
||||
break;
|
||||
case 'delete':
|
||||
if (num < 0) {
|
||||
this.widgetList.splice(historyObj.index, 0, historyObj.item);
|
||||
} else {
|
||||
this.widgetList.splice(historyObj.index, 1);
|
||||
}
|
||||
break;
|
||||
case 'modify':
|
||||
if (num < 0) {
|
||||
this.$set(this.widgetList, historyObj.index, {
|
||||
...historyObj.item,
|
||||
style: historyObj.item.history.style,
|
||||
content: historyObj.item.history.content
|
||||
});
|
||||
} else {
|
||||
this.$set(this.widgetList, historyObj.index, {
|
||||
...historyObj.item
|
||||
});
|
||||
}
|
||||
}
|
||||
if (num < 0) {
|
||||
this.historyIndex += num;
|
||||
}
|
||||
// this.widgetHistory[this.historyIndex - 1]
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
activeWidget() {
|
||||
return this.widgetList.find(w => w.active);
|
||||
},
|
||||
sortWidgetList() {
|
||||
return this.widgetList
|
||||
.slice(0)
|
||||
.sort((a, b) => b.style.zIndex - a.style.zIndex);
|
||||
},
|
||||
canClickPrev() {
|
||||
// 是否能点击上一步
|
||||
return this.historyIndex > -1;
|
||||
},
|
||||
canClickNext() {
|
||||
return (
|
||||
this.historyIndex < this.widgetHistory.length - 1 &&
|
||||
this.widgetHistory.length > 0
|
||||
);
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
widgetList: {
|
||||
handler(val) {
|
||||
zIndex = val.length;
|
||||
val.forEach((v, i) => {
|
||||
v.style.zIndex = i + 1;
|
||||
});
|
||||
this.$emit('input', {
|
||||
background: this.value.background,
|
||||
widgetList: this.widgetList
|
||||
});
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
'value.widgetList': {
|
||||
handler(val) {
|
||||
if (val !== this.widgetList) {
|
||||
this.widgetList = val;
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
this.getDZoom();
|
||||
this.handleResizeFn = _.throttle(this.getDZoom, 100);
|
||||
window.addEventListener('resize', this.handleResizeFn);
|
||||
},
|
||||
beforeDestroy() {
|
||||
window.removeEventListener('resize', this.handleResizeFn);
|
||||
},
|
||||
created() {
|
||||
this.widgetList = this.value.widgetList || [];
|
||||
this.widgetList.forEach(v => {
|
||||
v.history = {
|
||||
content: v.content,
|
||||
style: _.cloneDeep(v.style)
|
||||
};
|
||||
});
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.w-60 {
|
||||
width: 60px !important;
|
||||
}
|
||||
.poster-editor-comp {
|
||||
display: flex;
|
||||
min-width: 830px;
|
||||
.main-pane {
|
||||
position: relative;
|
||||
flex: 1;
|
||||
padding: 0 16px;
|
||||
.header-ct {
|
||||
display: flex;
|
||||
height: 40px;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.mirror {
|
||||
transform: rotateY(180deg);
|
||||
}
|
||||
}
|
||||
.center {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
.poster-content {
|
||||
top: 40px;
|
||||
left: 50%;
|
||||
position: absolute;
|
||||
/*min-width: 480px;
|
||||
min-height: 360px;
|
||||
max-width: 960px;
|
||||
max-height: 720px;*/
|
||||
width: 960px;
|
||||
height: 720px;
|
||||
/* margin: 0 auto;*/
|
||||
transform: translateX(-50%);
|
||||
&:hover {
|
||||
outline: 1px solid grey;
|
||||
}
|
||||
transform-origin: 0 0;
|
||||
background-size: 100% 100% !important;
|
||||
.bg-puppet {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
.right-pane {
|
||||
box-sizing: border-box;
|
||||
width: 295px;
|
||||
padding-left: 16px;
|
||||
border: 1px solid #e4e4e4;
|
||||
background: #fff;
|
||||
z-index: 900;
|
||||
.header-ct {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
}
|
||||
.style-setting-ct {
|
||||
.style-config {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
padding-left: 16px;
|
||||
.label {
|
||||
display: inline-block;
|
||||
margin-left: 8px;
|
||||
width: 48px;
|
||||
text-align: left;
|
||||
}
|
||||
.other {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
font-size: 18px;
|
||||
color: rgba(0, 0, 0, 0.8);
|
||||
&.active {
|
||||
background: rgba(0, 0, 0, 0.8);
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.widget-zIndex-config-ct {
|
||||
.widget-list-item {
|
||||
cursor: move;
|
||||
}
|
||||
.item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 40px;
|
||||
border-bottom: 1px solid #e1e1e1;
|
||||
font-size: 14px;
|
||||
.delete-btn {
|
||||
position: absolute;
|
||||
display: none;
|
||||
transition: all 200ms linear;
|
||||
}
|
||||
&:hover .delete-btn {
|
||||
display: block;
|
||||
}
|
||||
.view {
|
||||
margin-left: 40px;
|
||||
width: 24px;
|
||||
height: 18px;
|
||||
background-size: 100% 100% !important;
|
||||
border: 1px solid rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.desc {
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
.bg {
|
||||
.view {
|
||||
margin-left: 24px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
33
src/components/poster-editor/textWidget.vue
Normal file
33
src/components/poster-editor/textWidget.vue
Normal file
@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
transform: `scale(${needScale ? dZoom : 1})`
|
||||
}"
|
||||
class="text-widget-ct">
|
||||
{{value}}
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
required: true
|
||||
},
|
||||
// 解决chrome字体无法更小的问题
|
||||
needScale: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.text-widget-ct {
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
95
src/components/poster-editor/widget.vue
Normal file
95
src/components/poster-editor/widget.vue
Normal file
@ -0,0 +1,95 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
...getStyleRaw
|
||||
}"
|
||||
class="widget-ct cursor-p"
|
||||
>
|
||||
<widgetWrapper
|
||||
:d-zoom="dZoom"
|
||||
:page="page"
|
||||
:show-wrapper="showWrapper"
|
||||
:widget-data="widgetData"
|
||||
:widget-name="widgetData.type"
|
||||
>
|
||||
<vue-qr text="123" :size="140"></vue-qr>
|
||||
</widgetWrapper>
|
||||
<widgetWrapper
|
||||
:d-zoom="dZoom"
|
||||
:page="page"
|
||||
:show-wrapper="showWrapper"
|
||||
:widget-data="widgetData"
|
||||
:widget-name="widgetData.type"
|
||||
>
|
||||
<component
|
||||
:need-scale="widgetData.type === 'text' && !showWrapper"
|
||||
:d-zoom="dZoom"
|
||||
v-model="widgetData.content"
|
||||
:is="childName"
|
||||
></component>
|
||||
</widgetWrapper>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import VueQr from 'vue-qr';
|
||||
import widgetWrapper from './widgetWrapper';
|
||||
import textWidget from './textWidget';
|
||||
import imgWidget from './imgWidget';
|
||||
export default {
|
||||
created() {
|
||||
console.log(this.dZoom);
|
||||
console.log(this.widgetData);
|
||||
console.log(this.showWrapper);
|
||||
console.log(this.page);
|
||||
},
|
||||
props: {
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
widgetData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
order: {
|
||||
type: Number
|
||||
},
|
||||
showWrapper: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
page: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
getStyleRaw() {
|
||||
let obj = {};
|
||||
let style = this.widgetData.style;
|
||||
let zIndex = style.zIndex;
|
||||
for (let key in style) {
|
||||
obj[key] =
|
||||
typeof style[key] === 'number'
|
||||
? style[key] * this.dZoom + 'px'
|
||||
: style[key];
|
||||
}
|
||||
obj.zIndex = zIndex;
|
||||
return obj;
|
||||
},
|
||||
childName() {
|
||||
return this.widgetData.type + 'Widget';
|
||||
}
|
||||
},
|
||||
components: {
|
||||
widgetWrapper,
|
||||
textWidget,
|
||||
imgWidget,
|
||||
VueQr
|
||||
}
|
||||
};
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.widget-ct {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
94
src/components/poster-editor/widgetInitDataMap.js
Normal file
94
src/components/poster-editor/widgetInitDataMap.js
Normal file
@ -0,0 +1,94 @@
|
||||
const setDataRangeMap = {
|
||||
text: {
|
||||
box: {
|
||||
show: true,
|
||||
detail: {
|
||||
top: true,
|
||||
left: true,
|
||||
height: true,
|
||||
width: true,
|
||||
background: true,
|
||||
zIndex: true
|
||||
}
|
||||
},
|
||||
content: {
|
||||
show: true
|
||||
},
|
||||
innerStyle: {
|
||||
show: true,
|
||||
detail: {
|
||||
align: true,
|
||||
lineHeight: true,
|
||||
fontSize: true,
|
||||
color: true,
|
||||
letterSpacing: true,
|
||||
isOblique: true,
|
||||
isBold: true
|
||||
}
|
||||
}
|
||||
},
|
||||
img: {
|
||||
box: {
|
||||
show: true,
|
||||
detail: {
|
||||
top: true,
|
||||
left: true,
|
||||
height: true,
|
||||
width: true,
|
||||
background: false,
|
||||
zIndex: true
|
||||
}
|
||||
},
|
||||
content: {
|
||||
show: false
|
||||
},
|
||||
innerStyle: {
|
||||
show: false,
|
||||
detail: {
|
||||
align: true,
|
||||
lineHeight: true,
|
||||
fontSize: true,
|
||||
color: true,
|
||||
letterSpacing: true,
|
||||
isOblique: true,
|
||||
isBold: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const defaultDataMap = {
|
||||
text: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
height: 40,
|
||||
width: 100,
|
||||
align: {
|
||||
x: 2,
|
||||
y: 2
|
||||
},
|
||||
// textAlign: 'center',
|
||||
lineHeight: 14,
|
||||
fontSize: 14,
|
||||
background: 'rgba(0, 0, 0, 0)',
|
||||
color: 'rgba(0, 0, 0, 1)',
|
||||
letterSpacing: 0,
|
||||
isOblique: false,
|
||||
isBold: false
|
||||
},
|
||||
img: {
|
||||
top: 0,
|
||||
left: 0,
|
||||
height: 40,
|
||||
width: 100,
|
||||
align: {
|
||||
x: 2,
|
||||
y: 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
setDataRangeMap,
|
||||
defaultDataMap
|
||||
}
|
||||
240
src/components/poster-editor/widgetWrapper.vue
Normal file
240
src/components/poster-editor/widgetWrapper.vue
Normal file
@ -0,0 +1,240 @@
|
||||
<template>
|
||||
<div
|
||||
:style="{
|
||||
'font-weight': widgetData.style.isBold ? 'bold' : 'normal',
|
||||
'font-style': widgetData.style.isOblique ? 'oblique' : 'normal',
|
||||
'justify-content': alignMap[widgetData.style.align.x],
|
||||
'align-items': alignMap[widgetData.style.align.y]
|
||||
}"
|
||||
:class="{active: showWrapper && widgetData.active}"
|
||||
class="widget-wrapper">
|
||||
<template v-if="showWrapper && widgetData.active">
|
||||
<div
|
||||
@mousedown.stop="handleMouseDown($event, item.cursor)"
|
||||
:style="item"
|
||||
:key="i"
|
||||
v-for="(item, i) in sizeControls"
|
||||
class="square"></div>
|
||||
</template>
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
widgetData: {
|
||||
type: Object,
|
||||
required: true
|
||||
},
|
||||
showWrapper: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
dZoom: {
|
||||
type: Number,
|
||||
required: true
|
||||
},
|
||||
page: {
|
||||
type: Object
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 调整尺寸用的
|
||||
sizeControls: [
|
||||
// 左上
|
||||
{
|
||||
top: 0,
|
||||
left: 0,
|
||||
cursor: 'nw-resize'
|
||||
},
|
||||
{
|
||||
top: 0,
|
||||
left: '50%',
|
||||
cursor: 'n-resize'
|
||||
},
|
||||
{
|
||||
top: 0,
|
||||
left: '100%',
|
||||
cursor: 'ne-resize'
|
||||
},
|
||||
{
|
||||
top: '50%',
|
||||
left: '100%',
|
||||
cursor: 'e-resize'
|
||||
},
|
||||
{
|
||||
top: '100%',
|
||||
left: '100%',
|
||||
cursor: 'se-resize'
|
||||
},
|
||||
{
|
||||
top: '100%',
|
||||
left: '50%',
|
||||
cursor: 's-resize'
|
||||
},
|
||||
{
|
||||
top: '100%',
|
||||
left: 0,
|
||||
cursor: 'sw-resize'
|
||||
},
|
||||
{
|
||||
top: '50%',
|
||||
left: 0,
|
||||
cursor: 'w-resize'
|
||||
}
|
||||
],
|
||||
dMouseXY: {},
|
||||
dActiveWidgetXY: {},
|
||||
dResizeWH: {},
|
||||
dirs: [],
|
||||
// 对齐方式
|
||||
alignMap: {
|
||||
1: 'flex-start',
|
||||
2: 'center',
|
||||
3: 'flex-end'
|
||||
},
|
||||
isMoving: false
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
handleMouseDown (e, cursor) {
|
||||
const dirMap = {
|
||||
n: 'top',
|
||||
s: 'bottom',
|
||||
e: 'right',
|
||||
w: 'left'
|
||||
}
|
||||
let first = cursor[0]
|
||||
let second = cursor[1]
|
||||
this.dirs = []
|
||||
this.dirs.push(dirMap[first])
|
||||
if (/[swen]/.test(second)) {
|
||||
this.dirs.push(dirMap[second])
|
||||
}
|
||||
e.stopPropagation()
|
||||
this.initDResize({
|
||||
startX: e.pageX,
|
||||
startY: e.pageY,
|
||||
originX: this.widgetData.style.left,
|
||||
originY: this.widgetData.style.top,
|
||||
width: this.widgetData.style.width,
|
||||
height: this.widgetData.style.height
|
||||
})
|
||||
document.addEventListener('mousemove', this.handleMouseMove, true)
|
||||
document.addEventListener('mouseup', this.handleMouseUp, true)
|
||||
},
|
||||
initDResize (payload) {
|
||||
let mouseXY = this.dMouseXY
|
||||
let widgetXY = this.dActiveWidgetXY
|
||||
let resizeWH = this.dResizeWH
|
||||
mouseXY.x = payload.startX
|
||||
mouseXY.y = payload.startY
|
||||
widgetXY.x = payload.originX
|
||||
widgetXY.y = payload.originY
|
||||
resizeWH.width = payload.width
|
||||
resizeWH.height = payload.height
|
||||
},
|
||||
// 更新组件宽高
|
||||
dResize ({ x, y, dirs }) {
|
||||
let page = this.page
|
||||
let target = this.widgetData.style
|
||||
let mouseXY = this.dMouseXY
|
||||
let widgetXY = this.dActiveWidgetXY
|
||||
let resizeWH = this.dResizeWH
|
||||
let parent = this.$parent
|
||||
|
||||
if (target.parent !== '-1') {
|
||||
// parent = store.state.dWidgets.find(item => item.uuid === target.parent)
|
||||
}
|
||||
|
||||
let dx = x - mouseXY.x
|
||||
let dy = y - mouseXY.y
|
||||
|
||||
let left = 0
|
||||
let top = 0
|
||||
const minWidth = 20
|
||||
for (let i = 0; i < dirs.length; ++i) {
|
||||
let dir = dirs[i]
|
||||
|
||||
switch (dir) {
|
||||
case 'top':
|
||||
let t = widgetXY.y + Math.floor(dy / this.dZoom)
|
||||
top = Math.max(t, 0)
|
||||
top = Math.min(widgetXY.y + resizeWH.height - target.fontSize, top)
|
||||
target.height += (target.top - top)
|
||||
target.height = Math.max(target.height, target.fontSize)
|
||||
target.top = top
|
||||
break
|
||||
case 'bottom':
|
||||
top = Math.floor(dy / this.dZoom)
|
||||
target.height = Math.max(Math.min(resizeWH.height + top, page.height - target.top), 40)
|
||||
break
|
||||
case 'left':
|
||||
let tLeft = widgetXY.x + Math.floor(dx / this.dZoom)
|
||||
left = Math.max(tLeft, 0)
|
||||
target.width += (target.left - left)
|
||||
target.width = Math.max(target.width, minWidth)
|
||||
left = Math.min(widgetXY.x + resizeWH.width - minWidth, left)
|
||||
target.left = left
|
||||
break
|
||||
case 'right':
|
||||
left = Math.floor(dx / this.dZoom) // 实际偏移量
|
||||
target.width = Math.max(Math.min(resizeWH.width + left, page.width - target.left), minWidth)
|
||||
break
|
||||
}
|
||||
}
|
||||
if (parent.uuid !== '-1') {
|
||||
// store.dispatch('updateGroupSize', parent.uuid)
|
||||
}
|
||||
|
||||
// store.dispatch('reChangeCanvas')
|
||||
},
|
||||
// 组件调整结束
|
||||
stopDResize (store) {
|
||||
/* if (store.state.dResizeing) {
|
||||
store.dispatch('pushHistory')
|
||||
}
|
||||
store.state.dResizeing = false */
|
||||
},
|
||||
handleMouseMove (e) {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
||||
this.dResize({
|
||||
x: e.pageX,
|
||||
y: e.pageY,
|
||||
dirs: this.dirs
|
||||
})
|
||||
},
|
||||
|
||||
handleMouseUp () {
|
||||
document.removeEventListener('mousemove', this.handleMouseMove, true)
|
||||
document.removeEventListener('mouseup', this.handleMouseUp, true)
|
||||
this.stopDResize()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.widget-wrapper{
|
||||
display: flex;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
&.active, &.active:hover{
|
||||
border: 1px dashed;
|
||||
}
|
||||
.square{
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
z-index: 999;
|
||||
background-color: #fff;
|
||||
outline: 1px solid #3b74f1;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user