ゴール
ざっくりとした流れ
マークダウン入力エリアに画像をドラッグ&ドロップまたはクリップボードからペーストできる。
入力された画像はサーバにアップロードされ、レスポンスとしてURLが返ってくる。
URLを元に、マークダウン入力エリアに画像表示のマークダウン記法を入力する
スクリプト
イベント
<textarea placeholder="# 本文" rows=24
:value="this.post.body"
@input="update"
@drop.prevent="handleDrop"
@paste="handlePaste"></textarea>
ドロップ時の処理
handleDrop: function(e) {
var files = e.dataTransfer.files
if (files.length > 0) {
var formData = new FormData()
for (var file of files) {
if (file.type.match('image.*')) {
formData.append('note', file)
}
}
this.postImage(formData)
}
クリップボードからペースト時の処理
handlePaste: function(e) {
var items = e.clipboardData.items
var formData = new FormData()
for (var item of items) {
if (item.type.match('image.*')) {
formData.append('note', item.getAsFile())
}
}
if (formData.has('note')) {
this.postImage(formData)
}
画像アップロード&マークダウン挿入
画像をアップロードしたらレスポンスで画像のURLを受け取る
textarea内のキャレットの位置はtextarea.selectionStartまたはtextarea.selectionEndで取得する。
それぞれ選択範囲の開始位置と終了位置を返すものだが、選択がない場合はどちらもキャレットの位置を返す
textarea.valueの文字列を操作してキャレット位置に画像表示のマークダウンを挿入する。
postImage: function(formData) {
axios
.post(URL, formData)
.then(response => {
var textarea = document.querySelector('textarea')
var sentence = textarea.value
var len = sentence.length
var pos = textarea.selectionStart
var before = sentence.substr(0, pos)
var after = sentence.substr(pos, len)
for (var image of response.data.images) {
var word = '![image](' + image + ')'
sentence = before + word + after
this.post.body = sentence
}
})
.catch(error => {
console.log(error)
})
},
全体
markdown.vue<template>
<form>
<textarea placeholder="# 本文" rows="24"
@input="update"
@drop.prevent="handleDrop"
@paste="handlePaste">
<div v-html="compiledMarkdown"></div>
</form>
</template>
<script>
import marked from 'marked'
import Prism from 'prismjs'
import lodash from 'lodash'
export default{
data: function(){
return{
title: '',
body: '',
}
},
computed: {
compiledMarkdown: function () {
return marked(this.body, { sanitize: true })
}
},
methods: {
update: lodash.debounce(function (e) {
this.body = e.target.value
}, 500),
postImage: function(formData) {
axios
.post(URL + 'upload/image/', formData)
.then(response => {
var textarea = document.querySelector('textarea')
var sentence = textarea.value
var len = sentence.length
var pos = textarea.selectionStart
var before = sentence.substr(0, pos)
var after = sentence.substr(pos, len)
for (var image of response.data.images) {
var word = '![image](' + image + ')'
sentence = before + word + after
this.post.body = sentence
}
})
.catch(error => {
console.log(error)
})
},
handleDrop: function(e) {
var files = e.dataTransfer.files
if (files.length > 0) {
var formData = new FormData()
for (var file of files) {
if (file.type.match('image.*')) {
formData.append('note', file)
}
}
this.postImage(formData)
}
},
handlePaste: function(e) {
var items = e.clipboardData.items
var formData = new FormData()
for (var item of items) {
if (item.type.match('image.*')) {
formData.append('note', item.getAsFile())
}
}
if (formData.has('note')) {
this.postImage(formData)
}
}
}
}
</script>
課題