前端实现图片多选

在文档扫描过程中,我们可能需要选择多个图像以进行重新排序、编辑和导出等操作。这时我们通常需要一个支持多选的文档查看器。

Dynamsoft Document Viewer是一个提供该种功能的SDK。它提供了一组查看器用于文档相关的操作。在本文中,我们将演示如何使用它来浏览和选择多张图片。此外,我们还将探讨如何从头实现这样一个查看器。

使用Dynamsoft Document Viewer浏览和选择多个图像

  1. 创建一个包含以下模板的新HTML文件。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
  <title>Browse Viewer</title>
</head>
<style>
</style>
<body>
</body>
<script>
</script>
</html>

在页面中包含Dynamsoft Document Viewer的文件。

<script src="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/ddv.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/ddv.css">

使用许可证初始化Dynamsoft Document Viewer。可以在这里申请一个证书。

Dynamsoft.DDV.Core.license = "DLS2eyJoYW5kc2hha2VDb2RlIjoiMjAwMDAxLTE2NDk4Mjk3OTI2MzUiLCJvcmdhbml6YXRpb25JRCI6IjIwMDAwMSIsInNlc3Npb25QYXNzd29yZCI6IndTcGR6Vm05WDJrcEQ5YUoifQ=="; //one-day trial
Dynamsoft.DDV.Core.engineResourcePath = "https://cdn.jsdelivr.net/npm/dynamsoft-document-viewer@1.1.0/dist/engine";// Lead to a folder containing the distributed WASM files
await Dynamsoft.DDV.Core.init();

创建一个新的文档实例。

const docManager = Dynamsoft.DDV.documentManager;
const doc = docManager.createDocument();

创建一个Browse Viewer实例,将其绑定到一个容器,然后用它来查看我们刚刚创建的文档。

HTML:

<div id="viewer"></div>

JavaScript:

const browseViewer = new Dynamsoft.DDV.BrowseViewer({
  container: document.getElementById("viewer"),
});

browseViewer.openDocument(doc.uid);

CSS:

#viewer {
  width: 320px;
  height: 480px;
}

使用input选择多个图像文件并将其加载到文档实例中,然后可以用Browse Viewer进行查看。

HTML:

<label>
  Select images to load:
  <br/>
  <input type="file" id="files" name="files" multiple onchange="filesSelected()"/>
</label>

JavaScript:

async function filesSelected(){
  let filesInput = document.getElementById("files");
  let files = filesInput.files;
  if (files.length>0) {
    for (let index = 0; index < files.length; index++) {
      const file = files[index];
      const blob = await readFileAsBlob(file);
      await doc.loadSource(blob); // load image
    }    
  }
}

function readFileAsBlob(file){
  return new Promise((resolve, reject) => {
    const fileReader = new FileReader();
    fileReader.onload = async function(e){
      const response = await fetch(e.target.result);
      const blob = await response.blob();
      resolve(blob);
    };
    fileReader.onerror = function () {
      reject('oops, something went wrong.');
    };
    fileReader.readAsDataURL(file);
  })
}

我们可以使用热键进行多选。按住CTRL键可选择和取消选择一个图像,按住SHIFT键可选择一系列图像。

演示视频

Browse Viewer内部使用Canvas实现,有较好的性能。

从头开始实现支持多选的查看器

那么我们可以如何实现多选?下面是分步实现过程。

  1. 创建一个容器作为查看器。它会列出文档图像的缩略图。

<div id="viewer">
  <div class="thumbnail selected">
    <img src="blob:http://127.0.0.1:8000/d837ea7d-56aa-4d7c-baba-dde7503b72dd" style="height: 168px;">
  </div>
  <div class="thumbnail">
    <img src="blob:http://127.0.0.1:8000/62819aff-5f5b-4766-9b5c-92586bace2d6" style="height: 168px;">
  </div>
</div>

样式:

它使用flex布局来对齐组件。

查看器的CSS:

#viewer {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  width: 320px;
  height: 480px;
  overflow: auto;
  background: lightgray;
  border: 1px solid black;
}

缩略图的CSS:

:root {
  --thumbnail-width: 140px;
}

.thumbnail {
  display: inline-flex;
  width: var(--thumbnail-width);
  height: 200px;
  padding: 5px;
  margin: 5px;
  align-items: center;
  justify-content: center;
}

.thumbnail:hover {
  background: gray;
}

.thumbnail.selected {
  background: gray;
}

.thumbnail.selected img {
  border: 1px solid orange;
}

.thumbnail img {
  width: 100%;
  max-height: 100%;
  object-fit: contain;
  border: 1px solid transparent;
}

从所选文件加载图像。根据图像比率及其容器的宽度对图像元素的高度进行调整。

let thumbnailWidth = 130;
async function filesSelected(){
  let filesInput = document.getElementById("files");
  let files = filesInput.files;
  if (files.length>0) {
    for (let index = 0; index < files.length; index++) {
      const file = files[index];
      const blob = await readFileAsBlob(file);
      const url = URL.createObjectURL(blob);
      appendImage(url);
    }
  }
}

function appendImage(url){
  let viewer = document.getElementById("viewer");
  let thumbnailContainer = document.createElement("div");
  thumbnailContainer.className = "thumbnail";
  let img = document.createElement("img");
  img.src = url;
  img.onload = function(){
    let height = thumbnailWidth/(img.naturalWidth/img.naturalHeight);
    img.style.height = Math.floor(height) + "px";
  }
  thumbnailContainer.appendChild(img);
  viewer.appendChild(thumbnailContainer);
}

根据是否存在滚动条调整缩略图的宽度。这里,我们通过更改自定义CSS属性来实现这一点。

function updateWidthBaseOnScrollBar(){
  let viewer = document.getElementById("viewer");
  if (viewer.scrollHeight>viewer.clientHeight) {
    let scrollBarWidth = viewer.offsetWidth - viewer.clientWidth;
    let width = 140 - Math.ceil(scrollBarWidth/2);
    document.documentElement.style.setProperty('--thumbnail-width', width + "px");
  }else{
    document.documentElement.style.setProperty('--thumbnail-width', "140px");
  }
}

为缩略图添加单击事件以选择多个图像。

thumbnailContainer.addEventListener("click",function(){
  const isMultiSelect = event.ctrlKey || event.metaKey;
  const isRangeSelect = event.shiftKey;
  const index = getIndex(thumbnailContainer);
  if (isMultiSelect) {
    toggleSelection(thumbnailContainer);
  } else if (isRangeSelect && lastSelectedIndex !== -1) {
    const firstSelectedIndex = getFirstSelectedIndex();
    if (firstSelectedIndex != -1) {
      selectRange(firstSelectedIndex, index);
    }else{
      selectRange(lastSelectedIndex, index);
    }
  } else {
    clearSelection();
    selectOne(thumbnailContainer);
  }
  lastSelectedIndex = index;
})

如果没有按下任何键,选择被点击的单张图。

clearSelection();
selectOne(thumbnailContainer);

function selectOne(thumbnail) {
  thumbnail.classList.add('selected');
}

function clearSelection() {
  let thumbnails = document.querySelectorAll(".thumbnail");
  thumbnails.forEach(thumbnail => thumbnail.classList.remove('selected'));
}

如果按下CTRL键,则切换被单击图的选择状态。

function toggleSelection(thumbnail) {
  thumbnail.classList.toggle('selected');
}

如果按下SHIFT键,则选中从第一个被选图到当前被点击的图的所有图片。

const firstSelectedIndex = getFirstSelectedIndex();
if (firstSelectedIndex != -1) {
  selectRange(firstSelectedIndex, index);
}else{
  selectRange(lastSelectedIndex, index);
}

function selectRange(start, end) {
  let thumbnails = document.querySelectorAll(".thumbnail");
  clearSelection();
  const [startIndex, endIndex] = start < end ? [start, end] : [end, start];
  for (let i = startIndex; i <= endIndex; i++) {
    selectOne(thumbnails[i]);
  }
}

源代码

github.com/tony-xlh/do…

 

原文:https://juejin.cn/post/7400566246434947122

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值