Skip to content

Commit 5edbcfa

Browse files
committed
图片合成:
信息 叠加的文本内容可生成图片并下载 功能
1 parent 1be7554 commit 5edbcfa

File tree

6 files changed

+112
-23
lines changed

6 files changed

+112
-23
lines changed

src/components/ElementDrr/index.vue

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
</template>
2222

2323
<script>
24-
import VueDrr from 'vue-drr'
24+
import VueDrr from '../../components/VueDrr'
2525
2626
export default {
2727
name: 'ElementDrr',
@@ -39,7 +39,6 @@ export default {
3939
element: {
4040
handler (val) {
4141
this.$nextTick(() => {
42-
console.log(val)
4342
this.updateHeight()
4443
})
4544
},

src/components/ImageRichText/index.vue

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -64,9 +64,7 @@ export default {
6464
},
6565
// 光标聚焦
6666
handleFocus () {
67-
this.$nextTick(() => {
68-
this.activeEleText.active = true
69-
})
67+
this.activeEleText.active = true
7068
}
7169
}
7270
}
@@ -75,7 +73,8 @@ export default {
7573
<style lang="less">
7674
.image-rich-text {
7775
min-height: 18px;
78-
padding: 6px 8px;
76+
padding: 6px 8px!important;
77+
outline: none;
7978
word-break: break-all;
8079
}
8180
</style>

src/components/TextSetting/index.vue

Lines changed: 63 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,14 @@
6161
</div>
6262
</el-form-item>
6363
<el-form-item>
64-
<el-button type="primary" @click="createImage">文本生成图片</el-button>
64+
<el-button type="primary" @click="downloadImage">文本生成图片</el-button>
6565
</el-form-item>
6666
</el-form>
6767
</div>
6868
</template>
6969

7070
<script>
71+
import { base64toBlob } from '../../utils'
7172
import ImageRichText from '../ImageRichText'
7273
7374
const fontFamilyData = [
@@ -151,7 +152,67 @@ export default {
151152
}
152153
},
153154
// 文本生成图片
154-
createImage () {}
155+
textBecomeImg (obj) {
156+
let canvas = document.createElement('canvas')
157+
canvas.width = obj.w
158+
canvas.height = obj.h
159+
let context = canvas.getContext('2d')
160+
// 绘制字体距离canvas顶部初始的高度
161+
let initTop = 4
162+
let initLeft = 8
163+
// 设置背景色
164+
context.fillStyle = obj.style.backgroundColor || 'transparent'
165+
context.fillRect(0, 0, canvas.width, canvas.height)
166+
// 设置文本样式
167+
context.fillStyle = obj.style.color
168+
context.font = obj.style.fontWeight + ' ' + obj.style.fontSize + ' ' + obj.style.fontFamily
169+
context.textBaseline = 'top'
170+
// 设置文本对齐方式
171+
context.textAlign = obj.style.textAlign
172+
173+
let textArr = obj.text.split('')
174+
let tempStr = ''
175+
let rowArr = []
176+
let maxTextWidth = canvas.width - 2 * initLeft
177+
178+
for (let i = 0; i < textArr.length; i++) {
179+
if (context.measureText(tempStr).width < maxTextWidth && context.measureText(tempStr + (textArr[i])).width <= maxTextWidth) {
180+
tempStr += textArr[i]
181+
} else {
182+
rowArr.push(tempStr)
183+
tempStr = textArr[i]
184+
}
185+
}
186+
rowArr.push(tempStr)
187+
188+
let drawLeft
189+
if (context.textAlign === 'left') {
190+
drawLeft = initLeft
191+
} else if (context.textAlign === 'center') {
192+
drawLeft = maxTextWidth / 2
193+
} else if (context.textAlign === 'right') {
194+
drawLeft = maxTextWidth
195+
}
196+
197+
for (let i = 0; i < rowArr.length; i++) {
198+
context.fillText(rowArr[i], drawLeft, (parseInt(obj.style.fontSize) * i + initTop), maxTextWidth)
199+
}
200+
201+
return canvas.toDataURL('image/png')
202+
},
203+
// 下载生成的图片
204+
downloadImage () {
205+
const imageBase64 = this.textBecomeImg(this.activeEleText)
206+
const imageBlob = base64toBlob(imageBase64)
207+
const link = document.createElement('a')
208+
const event = document.createEvent('HTMLEvents')
209+
// initEvent 不加后两个参数会在火狐下报错 事件类型,是否冒泡,是否阻止浏览器的默认行为
210+
event.initEvent('click', true, true)
211+
link.download = 'image.png'
212+
link.href = URL.createObjectURL(imageBlob)
213+
// 兼容火狐
214+
link.dispatchEvent(new MouseEvent('click', {bubbles: true, cancelable: true, view: window}))
215+
}
155216
}
156217
}
157218
</script>

src/utils/index.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,17 @@ export function keepCursorEnd (target) {
8989
range.select()
9090
}
9191
}
92+
93+
/**
94+
* 将base64图片数据转换成Blob
95+
* @param image
96+
* @returns {Blob}
97+
*/
98+
export function base64toBlob (image) {
99+
let bytes = window.atob(image.split(',')[1])
100+
let array = []
101+
for (let i = 0; i < bytes.length; i++) {
102+
array.push(bytes.charCodeAt(i))
103+
}
104+
return new Blob([new Uint8Array(array)], {type: 'image/png'})
105+
}

src/views/image/ImageCropper.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@
3535
<div class="setting-box">
3636
<UploadImage @on-success="handleSuccess"/>
3737
<el-button size="small" type="primary" style="margin-top: 20px">
38-
<a @click="downloadImage()">生成图片</a>
38+
<a @click="downloadImage">生成图片</a>
3939
</el-button>
4040
<a :href="downImg" download="demo.png" ref="downloadDom"></a>
4141
</div>

src/views/image/ImageSynthesizer.vue

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
<!-- 图片 -->
2525
<img v-if="item.type==='image'" :src="item.src" draggable="false">
2626
<!-- 文字 -->
27-
<ImageRichText v-if="item.type === 'text'" v-model="item.text" :element="item"/>
27+
<ImageRichText v-if="item.type === 'text'" v-model="item.text" :element="item" :activeEleText="activeEleText"/>
2828
</ElementDrr>
2929
</div>
3030
</div>
@@ -45,7 +45,7 @@
4545
<UploadImage @on-success="handleAddImage"/>
4646
</el-form-item>
4747
<el-form-item label="删除元素">
48-
<el-button type="danger" @click="submit">删除元素</el-button>
48+
<el-button type="danger" @click="deleteActiveEle">删除元素</el-button>
4949
</el-form-item>
5050
</el-form>
5151
<TextSetting v-if="activeEle.type === 'text'" :activeEleText="activeEleText"/>
@@ -180,20 +180,24 @@ export default {
180180
x: 160,
181181
y: 100,
182182
w: 180,
183-
h: 26,
183+
h: 36,
184184
style: {
185185
textAlign: 'left',
186-
lineHeight: '14px',
187-
fontSize: '14px',
186+
lineHeight: '24px',
187+
fontSize: '24px',
188188
fontFamily: '微软黑体',
189189
fontWeight: 400,
190-
color: '#333',
190+
color: '#f70707',
191191
backgroundColor: '',
192-
overflowY: 'hidden'
192+
overflow: 'hidden'
193193
}
194194
}
195-
this.elements.push(text)
196-
this.updateActiveEle(text)
195+
if (this.elements.length > 4) {
196+
this.$message.warning('图片上最多叠加5个元素!')
197+
} else {
198+
this.elements.push(text)
199+
this.updateActiveEle(text)
200+
}
197201
},
198202
// 添加图片
199203
addImage (imgObj) {
@@ -209,9 +213,13 @@ export default {
209213
src: imgObj.src,
210214
size: imgObj.size
211215
}
212-
this.elements.push(image)
213-
if (imgObj.active) {
214-
this.updateActiveEle(image)
216+
if (this.elements.length > 4) {
217+
this.$message.warning('图片上最多叠加5个元素!')
218+
} else {
219+
this.elements.push(image)
220+
if (imgObj.active) {
221+
this.updateActiveEle(image)
222+
}
215223
}
216224
},
217225
// 上传图片成功
@@ -267,11 +275,19 @@ export default {
267275
h
268276
}
269277
},
278+
// 更新当前选中的元素
270279
updateActiveEle (element) {
271280
this.activeEle = element
272281
},
273-
submit () {
274-
console.log(this.elements)
282+
// 删除图片上当前选择的元素
283+
deleteActiveEle () {
284+
const newElements = this.elements.filter(item => {
285+
return item.tag !== this.activeEle.tag
286+
})
287+
this.$nextTick(() => {
288+
this.elements = newElements
289+
})
290+
this.updateActiveEle({})
275291
}
276292
}
277293
}

0 commit comments

Comments
 (0)