feat:image previewer
This commit is contained in:
@@ -109,6 +109,96 @@
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.image-previewer {
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
z-index: 1000;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
background: #f2f4f7;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
animation: previewerAnimation .3s;
|
||||
}
|
||||
|
||||
@keyframes previewerAnimation {
|
||||
0% {
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.image-previewer .image-close {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
right: 50px;
|
||||
top: 30px;
|
||||
z-index: 99;
|
||||
cursor: pointer;
|
||||
background: url(../images/close.svg) no-repeat;
|
||||
background-size: 100% 100%;
|
||||
transition: all .3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.image-previewer .image-close:hover {
|
||||
background-color: #e2e6ed;
|
||||
}
|
||||
|
||||
.image-previewer img {
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
.image-previewer .image-menu {
|
||||
height: 50px;
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
z-index: 99;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.image-previewer .image-menu i {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
margin-right: 15px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
transition: all .3s;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.image-previewer .image-menu i:hover {
|
||||
background-color: #e2e6ed;
|
||||
}
|
||||
|
||||
.image-previewer .image-menu i.zoom-in {
|
||||
background-image: url(../images/zoom-in.svg);
|
||||
}
|
||||
|
||||
.image-previewer .image-menu i.zoom-out {
|
||||
background-image: url(../images/zoom-out.svg);
|
||||
}
|
||||
|
||||
.image-previewer .image-menu i.rotate {
|
||||
background-image: url(../images/rotate.svg);
|
||||
}
|
||||
|
||||
.image-previewer .image-menu i.original-size {
|
||||
background-image: url(../images/original-size.svg);
|
||||
}
|
||||
|
||||
.table-tool__row {
|
||||
position: absolute;
|
||||
width: 12px;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg"><path d="M23.97 7l1.415 1.414-7.779 7.778 7.779 7.779-1.414 1.414-7.779-7.779-7.778 7.779L7 23.97l7.778-7.779L7 8.414 8.414 7l7.778 7.778L23.971 7z" fill="#3D4757" fill-rule="evenodd"/></svg>
|
||||
|
After Width: | Height: | Size: 254 B |
@@ -0,0 +1 @@
|
||||
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg"><path d="M4 4h24v24H4V4zm2 2v20h20V6H6zm4 5h2v10h-2V11zm5 2h2v2h-2v-2zm0 4h2v2h-2v-2zm5-6h2v10h-2V11z" fill="#3D4757"/></svg>
|
||||
|
After Width: | Height: | Size: 188 B |
@@ -0,0 +1 @@
|
||||
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg"><g fill="#3D4757" fill-rule="evenodd"><path d="M16 4c6.627 0 12 5.373 12 12a11.97 11.97 0 01-4 8.944V23h-.86A9.968 9.968 0 0026 16c0-5.523-4.477-10-10-10S6 10.477 6 16c0 5.185 3.947 9.449 9 9.95v2.009C8.84 27.451 4 22.291 4 16 4 9.373 9.373 4 16 4z" fill-rule="nonzero"/><path d="M19.879 27.328l1.767-6.717 4.95 4.95z"/></g></svg>
|
||||
|
After Width: | Height: | Size: 393 B |
@@ -0,0 +1 @@
|
||||
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg"><path d="M14 14v-4h2v4h4v2h-4v4h-2v-4h-4v-2h4zm8.749 10.163A11.952 11.952 0 0115 27C8.373 27 3 21.627 3 15S8.373 3 15 3s12 5.373 12 12c0 2.954-1.067 5.658-2.837 7.749l4.908 4.908-1.414 1.414-4.908-4.908zM15 25c5.523 0 10-4.477 10-10S20.523 5 15 5 5 9.477 5 15s4.477 10 10 10z" fill="#3D4757"/></svg>
|
||||
|
After Width: | Height: | Size: 362 B |
@@ -0,0 +1 @@
|
||||
<svg width="32" height="32" xmlns="http://www.w3.org/2000/svg"><path d="M22.749 24.163A11.952 11.952 0 0115 27C8.373 27 3 21.627 3 15S8.373 3 15 3s12 5.373 12 12c0 2.954-1.067 5.658-2.837 7.749l4.908 4.908-1.414 1.414-4.908-4.908zM15 25c5.523 0 10-4.477 10-10S20.523 5 15 5 5 9.477 5 15s4.477 10 10 10zm-5-11h10v2H10v-2z" fill="#3D4757"/></svg>
|
||||
|
After Width: | Height: | Size: 344 B |
@@ -12,6 +12,7 @@ export class ImageParticle {
|
||||
private curElement: IElement | null
|
||||
private curPosition: IElementPosition | null
|
||||
private imageCache: Map<string, HTMLImageElement>
|
||||
// 拖拽改变尺寸
|
||||
private resizerSelection: HTMLDivElement
|
||||
private resizerHandleList: HTMLDivElement[]
|
||||
private resizerImageContainer: HTMLDivElement
|
||||
@@ -21,6 +22,9 @@ export class ImageParticle {
|
||||
private mousedownX: number
|
||||
private mousedownY: number
|
||||
private curHandleIndex: number
|
||||
// 预览选区
|
||||
private previewerContainer: HTMLDivElement | null
|
||||
private previewerImage: HTMLImageElement | null
|
||||
|
||||
constructor(draw: Draw) {
|
||||
this.container = draw.getContainer()
|
||||
@@ -30,6 +34,7 @@ export class ImageParticle {
|
||||
this.curElement = null
|
||||
this.curPosition = null
|
||||
this.imageCache = new Map()
|
||||
// 图片尺寸缩放
|
||||
const { resizerSelection, resizerHandleList, resizerImageContainer, resizerImage } = this._createResizerDom()
|
||||
this.resizerSelection = resizerSelection
|
||||
this.resizerHandleList = resizerHandleList
|
||||
@@ -40,6 +45,10 @@ export class ImageParticle {
|
||||
this.mousedownX = 0
|
||||
this.mousedownY = 0
|
||||
this.curHandleIndex = 0 // 默认右下角
|
||||
// 图片预览
|
||||
resizerSelection.ondblclick = this._dblclick.bind(this)
|
||||
this.previewerContainer = null
|
||||
this.previewerImage = null
|
||||
}
|
||||
|
||||
private _createResizerDom(): IImageParticleCreateResult {
|
||||
@@ -54,7 +63,7 @@ export class ImageParticle {
|
||||
handleDom.style.background = this.options.resizerColor
|
||||
handleDom.classList.add(`handle-${i}`)
|
||||
handleDom.setAttribute('data-index', String(i))
|
||||
handleDom.onmousedown = this._handleMousedown.bind(this)
|
||||
handleDom.onmousedown = this._mousedown.bind(this)
|
||||
resizerSelection.append(handleDom)
|
||||
resizerHandleList.push(handleDom)
|
||||
}
|
||||
@@ -69,7 +78,7 @@ export class ImageParticle {
|
||||
return { resizerSelection, resizerHandleList, resizerImageContainer, resizerImage }
|
||||
}
|
||||
|
||||
private _handleMousedown(evt: MouseEvent) {
|
||||
private _mousedown(evt: MouseEvent) {
|
||||
this.canvas = this.draw.getPage()
|
||||
if (!this.curPosition || !this.curElement) return
|
||||
const { scale } = this.options
|
||||
@@ -156,6 +165,119 @@ export class ImageParticle {
|
||||
evt.preventDefault()
|
||||
}
|
||||
|
||||
private _dblclick() {
|
||||
this._drawPreviewer()
|
||||
document.body.style.overflow = 'hidden'
|
||||
}
|
||||
|
||||
private _drawPreviewer() {
|
||||
const previewerContainer = document.createElement('div')
|
||||
previewerContainer.classList.add('image-previewer')
|
||||
// 关闭按钮
|
||||
const closeBtn = document.createElement('i')
|
||||
closeBtn.classList.add('image-close')
|
||||
closeBtn.onclick = () => {
|
||||
this._clearPreviewer()
|
||||
}
|
||||
previewerContainer.append(closeBtn)
|
||||
// 图片
|
||||
const imgContainer = document.createElement('div')
|
||||
imgContainer.classList.add('image-container')
|
||||
const img = document.createElement('img')
|
||||
img.src = this.curElement?.value || ''
|
||||
img.draggable = false
|
||||
imgContainer.append(img)
|
||||
this.previewerImage = img
|
||||
previewerContainer.append(imgContainer)
|
||||
// 操作栏
|
||||
let scaleSize = 1
|
||||
let rotateSize = 0
|
||||
let translateX = 0
|
||||
let translateY = 0
|
||||
const menuContainer = document.createElement('div')
|
||||
menuContainer.classList.add('image-menu')
|
||||
const zoomIn = document.createElement('i')
|
||||
zoomIn.classList.add('zoom-in')
|
||||
zoomIn.onclick = () => {
|
||||
scaleSize += 0.1
|
||||
this._setPreviewerTransform(scaleSize, rotateSize, translateX, translateY)
|
||||
}
|
||||
menuContainer.append(zoomIn)
|
||||
const zoomOut = document.createElement('i')
|
||||
zoomOut.onclick = () => {
|
||||
if (scaleSize - 0.1 <= 0.1) return
|
||||
scaleSize -= 0.1
|
||||
this._setPreviewerTransform(scaleSize, rotateSize, translateX, translateY)
|
||||
}
|
||||
zoomOut.classList.add('zoom-out')
|
||||
menuContainer.append(zoomOut)
|
||||
const rotate = document.createElement('i')
|
||||
rotate.classList.add('rotate')
|
||||
rotate.onclick = () => {
|
||||
rotateSize += 1
|
||||
this._setPreviewerTransform(scaleSize, rotateSize, translateX, translateY)
|
||||
}
|
||||
menuContainer.append(rotate)
|
||||
const originalSize = document.createElement('i')
|
||||
originalSize.classList.add('original-size')
|
||||
originalSize.onclick = () => {
|
||||
scaleSize = 1
|
||||
rotateSize = 0
|
||||
translateX = 0
|
||||
translateY = 0
|
||||
this._setPreviewerTransform(scaleSize, rotateSize, translateX, translateY)
|
||||
}
|
||||
menuContainer.append(originalSize)
|
||||
previewerContainer.append(menuContainer)
|
||||
this.previewerContainer = previewerContainer
|
||||
document.body.append(previewerContainer)
|
||||
// 拖拽调整位置
|
||||
let startX = 0
|
||||
let startY = 0
|
||||
let isAllowDrag = false
|
||||
img.onmousedown = (evt) => {
|
||||
isAllowDrag = true
|
||||
startX = evt.x
|
||||
startY = evt.y
|
||||
previewerContainer.style.cursor = 'move'
|
||||
}
|
||||
previewerContainer.onmousemove = (evt: MouseEvent) => {
|
||||
if (!isAllowDrag) return
|
||||
translateX += (evt.x - startX)
|
||||
translateY += (evt.y - startY)
|
||||
startX = evt.x
|
||||
startY = evt.y
|
||||
this._setPreviewerTransform(scaleSize, rotateSize, translateX, translateY)
|
||||
}
|
||||
previewerContainer.onmouseup = () => {
|
||||
isAllowDrag = false
|
||||
previewerContainer.style.cursor = 'auto'
|
||||
}
|
||||
previewerContainer.onwheel = (evt) => {
|
||||
evt.preventDefault()
|
||||
if (evt.deltaY < 0) {
|
||||
// 放大
|
||||
scaleSize += 0.1
|
||||
} else {
|
||||
// 缩小
|
||||
if (scaleSize - 0.1 <= 0.1) return
|
||||
scaleSize -= 0.1
|
||||
}
|
||||
this._setPreviewerTransform(scaleSize, rotateSize, translateX, translateY)
|
||||
}
|
||||
}
|
||||
|
||||
public _setPreviewerTransform(scale: number, rotate: number, x: number, y: number) {
|
||||
if (!this.previewerImage) return
|
||||
this.previewerImage.style.transform = `scale(${scale}) rotate(${rotate * 90}deg) translate(${x}px,${y}px)`
|
||||
}
|
||||
|
||||
private _clearPreviewer() {
|
||||
this.previewerContainer?.remove()
|
||||
this.previewerContainer = null
|
||||
document.body.style.overflow = 'auto'
|
||||
}
|
||||
|
||||
public getImageCache(): Map<string, HTMLImageElement> {
|
||||
return this.imageCache
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user