使用拖放和 HTML5 画布创建缩略图

HTML5 Canvas 是一个非常酷的功能。它看似只是一个在浏览器内用低级 API 绘画的机会,但实际上你可以用它来大量转换和修改文档中的图像和视频内容。今天,让我们快速了解一下如何使用 Canvas 和 FileReader API 从拖放到浏览器文档中的图像创建缩略图。

最终代码在 GitHub 上,你可以在 这里查看在线演示。YouTube 上也有一段屏幕录制。

步骤 1:将文件导入浏览器

在浏览器中调整图像大小的第一步是获取它们。为此,我们可以在页面中添加一个元素,并为其分配拖放事件处理程序

s.addEventListener( 'dragover', function ( evt ) {
  evt.preventDefault();
}, false );
s.addEventListener( 'drop', getfiles, false );

请注意,我们只在将东西拖放到元素上时才阻止默认行为。这是为了防止浏览器在将图像拖入时只显示它们。

然后 getfiles() 函数完成繁重的工作,读取所有文件并将其发送到执行调整大小和图像生成的函数。

function getfiles( ev ) {
  var files = ev.dataTransfer.files;
  if ( files.length > 0 ) {
    var i = files.length;
    while ( i-- ) {
      var file = files[ i ];
      if ( file.type.indexOf( 'image' ) === -1 ) { continue; }
      var reader = new FileReader();
      reader.readAsDataURL( file );
      reader.onload = function ( ev ) {
        var img = new Image();
        img.src = ev.target.result;
        img.onload = function() {
        imagetocanvas( this, thumbwidth, thumbheight, crop, background );
        };
      };
    }
  }
  ev.preventDefault();
};

drop 事件提供了一个名为 dataTransfer 的属性,其中包含所有已拖放文件的列表。我们确保至少有一个文件被拖放,然后遍历它们。

如果文件类型不是图像(或者换句话说,文件的 type 属性不包含字符串“image”),我们不会对该文件进行任何操作,并继续循环。

如果文件是图像,我们实例化一个新的 FileReader 并告诉它将文件读取为数据 URL。当读取器成功加载文件时,它会触发其 onload 处理程序。

在这个处理程序中,我们创建一个新的图像,并将它的 src 属性设置为文件传输的 result。然后,我们将此图像发送到 imagetocanvas() 函数,并附带要调整大小的参数(在演示中,这些参数来自表单)。

function imagetocanvas( img, thumbwidth, thumbheight, crop, background ) {
  c.width = thumbwidth;
  c.height = thumbheight;
  var dimensions = resize( img.width, img.height, thumbwidth, thumbheight );
  if ( crop ) {
    c.width = dimensions.w;
    c.height = dimensions.h;
    dimensions.x = 0;
    dimensions.y = 0;
  }
  if ( background !== 'transparent' ) {
    cx.fillStyle = background;
    cx.fillRect ( 0, 0, thumbwidth, thumbheight );
  }
  cx.drawImage(
    img, dimensions.x, dimensions.y, dimensions.w, dimensions.h
  );
  addtothumbslist( jpeg, quality );
};

此函数获取所需的缩略图大小,并将画布调整为这些尺寸。这样做的好处是会清空画布,这样就不会将任何旧的图像数据添加到我们的缩略图中。然后,我们使用 resize() 函数将图像调整为适合缩略图。你可以在源代码中自己查看此函数的作用,它只是意味着图像会调整为适合。该函数返回一个对象,其中包含新图像的宽度和高度,以及它应该放置在画布上的 x 和 y 位置。

如果我们不想要全尺寸缩略图,而是要裁剪它,则相应调整画布大小,并将 x 和 y 重置为 0。

如果用户请求背景,我们用颜色填充画布。之后,我们将图像放置在画布上,使用 x 和 y 坐标以及新的宽度和高度。

这负责在画布上创建新的缩略图,但我们还没有将其作为文档中的图像获取。为此,我们调用 addtothumbslist()

function addtothumbslist( jpeg, quality ) {
  var thumb = new Image(),
      url = jpeg ? c.toDataURL( 'image/jpeg' , quality ) : c.toDataURL();
  thumb.src = url;
  thumb.title = Math.round( url.length / 1000 * 100 ) / 100 + ' KB';
  o.appendChild( thumb );
};

这个函数创建一个新的图像,并检查用户是否想要创建 JPG 或 PNG 图像。PNG 图像往往具有更好的质量,但文件大小也更大。如果请求了 JPG,我们调用画布的 toDataURL() 方法,并使用两个参数:请求的 JPEG MIME 类型和图像质量(介于 0 到 1 之间,1 为最佳质量)。如果需要 PNG,我们只需调用 toDataURL() 即可,无需任何参数,因为这是默认设置。

我们将图像的 src 设置为生成的 url 字符串,并添加一个标题,显示图像的大小(以 KB 为单位,四舍五入到两位小数)。接下来要做的就是将缩略图添加到页面上的输出元素中。

就是这样,你就可以将图像拖放到浏览器中以生成缩略图了。目前,我们只能一次保存一个缩略图(或者如果你有一些下载插件,则可以一次保存多个)。如果将 Zip.js 添加到混合中以将其作为 zip 提供,那会很有趣。我敢你试试!:)

更多阅读

关于 Chris Heilmann

HTML5 和开放网络的福音传道者。让我们修复它!

更多 Chris Heilmann 的文章…


6 条评论

  1. Beben Koben

    太酷了,老板…
    非常感谢!

    2012 年 2 月 2 日 下午 01:21

  2. Andrey

    很棒的演示!
    我想到一个改进,就是不使用 FileReader 将文件加载到图像,更好的方法是使用 Blob URI
    img.src = URL.createObjectURL(file)
    这样可以避免将文件转换为 base64,并且可能更节省内存。您尝试过这种方法吗?

    2012 年 2 月 3 日 上午 10:41

  3. mihai

    是否可以为视频文件创建缩略图?这将是一件非常棒的事情…图像缩略图可以很容易地在没有 HTML5 的情况下生成。

    2012 年 3 月 9 日 下午 08:44

  4. RabinsXp

    感谢您提供此实用技巧。

    2012 年 3 月 26 日 上午 02:22

  5. Jason

    这很棒,但会造成内存泄漏。如果您尝试拖放大量大型图像,img.src = ev.target.results 会消耗大量内存。它不会释放(在 Firefox 和 Chrome 中)。您对如何防止泄漏有什么想法吗?

    2012 年 11 月 11 日 下午 01:43

  6. Jason

    好的 - 我使用之前发布者建议的 createObjectURL 方法解决了该问题。我将它传递给 reader.onload,它会在加载后释放内存。我不知道为什么将 img.src 指向 thee.target.result 不会释放内存,但事实似乎就是这样。

    简单的更改,可能值得更新您的优秀文章?

    2012 年 11 月 12 日 上午 11:32

本文的评论已关闭。