From 4df0d03baa7352f24dd14a0a393ede87698bfea1 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Sat, 30 Jun 2018 22:16:51 +0800 Subject: [PATCH] WebView: click an image to view/zoom it --- src/resources/hoedown.js | 1 + src/resources/markdown-it.js | 1 + src/resources/markdown_template.html | 7 ++ src/resources/markdown_template.js | 5 +- src/resources/marked.js | 1 + src/resources/showdown.js | 1 + src/resources/view_image.css | 63 +++++++++++ src/resources/view_image.js | 149 +++++++++++++++++++++++++++ src/vnote.qrc | 2 + 9 files changed, 228 insertions(+), 2 deletions(-) create mode 100644 src/resources/view_image.css create mode 100644 src/resources/view_image.js diff --git a/src/resources/hoedown.js b/src/resources/hoedown.js index 42e4922d..3089a5a6 100644 --- a/src/resources/hoedown.js +++ b/src/resources/hoedown.js @@ -20,6 +20,7 @@ var updateHtml = function(html) { contentDiv.innerHTML = html; insertImageCaption(); + setupImageView(); var codes = document.getElementsByTagName('code'); mermaidIdx = 0; diff --git a/src/resources/markdown-it.js b/src/resources/markdown-it.js index 0ea8e3d0..4948cd13 100644 --- a/src/resources/markdown-it.js +++ b/src/resources/markdown-it.js @@ -138,6 +138,7 @@ var updateText = function(text) { contentDiv.innerHTML = html; handleToc(needToc); insertImageCaption(); + setupImageView(); handleMetaData(); renderMermaid('lang-mermaid'); renderFlowchart(['lang-flowchart', 'lang-flow']); diff --git a/src/resources/markdown_template.html b/src/resources/markdown_template.html index 62731b28..1535d34b 100644 --- a/src/resources/markdown_template.html +++ b/src/resources/markdown_template.html @@ -21,17 +21,24 @@ } + +
+ + diff --git a/src/resources/markdown_template.js b/src/resources/markdown_template.js index 83942d69..c241c509 100644 --- a/src/resources/markdown_template.js +++ b/src/resources/markdown_template.js @@ -854,7 +854,7 @@ var insertImageCaption = function() { captionDiv.textContent = img.alt; img.insertAdjacentElement('afterend', captionDiv); } -} +}; var asyncJobsCount = 0; @@ -1005,7 +1005,8 @@ window.onmousedown = function(e) { // Left button and Ctrl key. if (e.buttons == 1 && isCtrl - && window.getSelection().type != 'Range') { + && window.getSelection().type != 'Range' + && !isViewingImage()) { vds_oriMouseClientX = e.clientX; vds_oriMouseClientY = e.clientY; vds_readyToScroll = true; diff --git a/src/resources/marked.js b/src/resources/marked.js index bc1ffbd2..37d409bd 100644 --- a/src/resources/marked.js +++ b/src/resources/marked.js @@ -58,6 +58,7 @@ var updateText = function(text) { contentDiv.innerHTML = html; handleToc(needToc); insertImageCaption(); + setupImageView(); renderMermaid('lang-mermaid'); renderFlowchart(['lang-flowchart', 'lang-flow']); renderPlantUML('lang-puml'); diff --git a/src/resources/showdown.js b/src/resources/showdown.js index 74a957f0..e40cabba 100644 --- a/src/resources/showdown.js +++ b/src/resources/showdown.js @@ -97,6 +97,7 @@ var updateText = function(text) { contentDiv.innerHTML = html; handleToc(needToc); insertImageCaption(); + setupImageView(); highlightCodeBlocks(document, VEnableMermaid, VEnableFlowchart, diff --git a/src/resources/view_image.css b/src/resources/view_image.css new file mode 100644 index 00000000..f84952a3 --- /dev/null +++ b/src/resources/view_image.css @@ -0,0 +1,63 @@ +.view-image { + cursor: pointer; + transition: 0.3s; +} + +.modal-box { + display: none; + position: fixed; + z-index: 1000; + padding-top: 50px; + left: 0; + top: 0; + width: 100%; + height: 100%; + overflow: hidden; + background-color: rgb(68, 68, 68); + background-color: rgba(68, 68, 68, 0.9); +} + +.modal-content { + margin: auto; + display: block; + width: auto; + height: auto; + cursor: move; +} + +/* Add Animation */ +.modal-content { + -webkit-animation-name: zoom; + -webkit-animation-duration: 0.6s; + animation-name: zoom; + animation-duration: 0.6s; +} + +@-webkit-keyframes zoom { + from {-webkit-transform:scale(0)} + to {-webkit-transform:scale(1)} +} + +@keyframes zoom { + from {transform:scale(0)} + to {transform:scale(1)} +} + +/* The Close Button */ +span.modal-close { + position: absolute; + z-index: 1000; + top: 15px; + right: 35px; + color: #DADADA; + font-size: 40px; + font-weight: bold; + transition: 0.3s; +} + +span.modal-close:hover, +span.modal-close:focus { + color: #EEEEEE; + text-decoration: none; + cursor: pointer; +} diff --git a/src/resources/view_image.js b/src/resources/view_image.js new file mode 100644 index 00000000..6f0260d7 --- /dev/null +++ b/src/resources/view_image.js @@ -0,0 +1,149 @@ +var imageViewDiv = document.getElementById('image-view-div'); + +var viewImage = function(obj, image) { + image = !image ? obj.src : image; + + imageViewDiv.style.display = 'block'; + + var boxImage = document.getElementById('image-view'); + boxImage.src = image; + // Restore image-view. + boxImage.style.width = ''; + boxImage.style.position = ''; + boxImage.style.zIndex = ''; +}; + +var viewBoxImageMouseDown = false; +var viewBoxImageOffsetToMouse = [0, 0]; + +var closeImageViewBox = function() { + imageViewDiv.style.display = "none"; +}; + +var initImageViewBox = function() { + // Left and top in pixel. + var moveImage = function(img, left, top) { + if (img.style.position != 'absolute') { + img.style.position = 'absolute'; + img.style.zIndex = parseInt(document.getElementById('image-view-close').style.zIndex) - 1; + } + + img.style.left = left + 'px'; + img.style.top = top + 'px'; + }; + + // View box. + imageViewDiv.onclick = function(e) { + e = e || window.event; + var boxImage = document.getElementById('image-view'); + if (e.target.id != boxImage.id) { + // Click outside the image to close the box. + closeImageViewBox(); + } + + e.preventDefault(); + }; + + imageViewDiv.onwheel = function(e) { + e = e || window.event; + var ctrl = !!e.ctrlKey; + if (ctrl) { + return; + } + + var target = e.target; + if (!target || target.id != 'image-view') { + return; + } + + var rect = target.getBoundingClientRect(); + var centerX = e.clientX - rect.left; + var centerY = e.clientY - rect.top; + + var oriWidth = target.getAttribute('oriWidth'); + var oriHeight = target.getAttribute('oriWidth'); + if (!oriWidth) { + oriWidth = rect.width; + oriHeight = rect.height; + + target.setAttribute('oriWidth', oriWidth); + target.setAttribute('oriHeight', oriHeight); + } + + var step = Math.floor(oriWidth / 4); + + var value = e.wheelDelta || -e.detail; + // delta >= 0 is up, which will trigger zoom in. + var delta = Math.max(-1, Math.min(1, value)); + + var newWidth = rect.width + (delta < 0 ? -step : step); + if (newWidth < 200) { + e.preventDefault(); + return; + } + + var factor = newWidth / rect.width; + + target.style.width = newWidth + 'px'; + + // Adjust the image around the center point. + moveImage(target, e.clientX - centerX * factor, e.clientY - centerY * factor); + + e.preventDefault(); + }; + + // Content image. + var boxImage = document.getElementById('image-view'); + boxImage.onmousedown = function(e) { + e = e || window.event; + var target = this || e.target; + viewBoxImageMouseDown = true; + viewBoxImageOffsetToMouse = [ + target.offsetLeft - e.clientX, + target.offsetTop - e.clientY + ]; + e.preventDefault(); + }; + + boxImage.onmouseup = function(e) { + e = e || window.event; + viewBoxImageMouseDown = false; + e.preventDefault(); + }; + + boxImage.onmousemove = function(e) { + e = e || window.event; + var target = this || e.target; + if (viewBoxImageMouseDown) { + moveImage(target, e.clientX + viewBoxImageOffsetToMouse[0], e.clientY + viewBoxImageOffsetToMouse[1]); + } + + e.preventDefault(); + }; + + // Close button. + document.getElementById('image-view-close').onclick = closeImageViewBox; +}; + +initImageViewBox(); + +var setupImageView = function() { + closeImageViewBox(); + + var imgs = document.getElementsByTagName('img'); + for (var i = 0; i < imgs.length; ++i) { + var img = imgs[i]; + if (img.id == 'image-view') { + continue; + } + + img.classList.add('view-image'); + img.onclick = function() { + viewImage(this, this.src); + }; + } +}; + +var isViewingImage = function() { + return imageViewDiv.style.display == 'block'; +}; diff --git a/src/vnote.qrc b/src/vnote.qrc index 35a8dacf..00bf6367 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -261,5 +261,7 @@ resources/icons/tags.svg resources/icons/tag_explorer.svg resources/icons/tag.svg + resources/view_image.js + resources/view_image.css