Slider FileReader JS 多张图片上传(递增索引)

Posted

技术标签:

【中文标题】Slider FileReader JS 多张图片上传(递增索引)【英文标题】:Slider FileReader JS Multiple Image Upload (Incrementing Index) 【发布时间】:2016-02-03 05:34:33 【问题描述】:

我正在尝试制作一个将图像预览上传到滑块的 javascript 多图像上传器,但我遇到了一些问题。到目前为止,看起来我能够将图像上传到滑块中,但问题似乎发生在我的 i 变量上 - 当我尝试增加它时,它保持不变,不允许我的 nextprevious 工作中的滑块箭头。如果有人知道如何让这个滑块正常工作,我将不胜感激。

JS代码:

$('#_uploadImages').click(function() 
    $('#_imagesInput').click()
)

$('#_imagesInput').on('change', function() 
    handleFileSelect();
);

function handleFileSelect() 
    //Check File API support
    if (window.File && window.FileList && window.FileReader) 

        var files = event.target.files; //FileList object
        var output = document.getElementById("frames");

        for (var i = 0; i < files.length; i++) 
            var file = files[i];

            //Only pics
            if (!file.type.match('image')) continue;

            var picReader = new FileReader();
            picReader.addEventListener("load", function (event) 
                var picFile = event.target;

                console.log(event);

                current_i = i;
                prev_i = current_i - 1;
                next_i = current_i + 1;

                //var div = document.createElement("div");
                //div.innerhtml = div.innerHTML + "<img class='thumbnail' src='" + picFile.result + "'" + "title='" + picFile.name + "'/>";
                //output.insertBefore(div, null);

                ////output.innerHTML = output.innerHTML + "<img class='thumbnail' style='max-width:500px' src='" + picFile.result + "'" + "title=''/>";  // TODO: Enter Title
                output.innerHTML = output.innerHTML + '<li id="slide-' + current_i + '" class="slide">' + "<img src='" + picFile.result + "'" + "title=''/>" + '<nav>' + '<a class="prev" href="#slide-' + prev_i + '">&larr;</a>' + '<a class="next" href="#slide-' + next_i + '">&rarr;</a>' + '</nav>' + '<li>';  // TODO: Enter Title
            );
            //Read the image
            picReader.readAsDataURL(file);
        
        //output.innerHTML = output.innerHTML + '<li class="quicknav">' + '<ul>' + '<li><a href="#slide-1"></a></li>' + '<li><a href="#slide-2"></a></li>' + '<li><a href="#slide-3"></a></li>' + '</ul>' + '</li>'
     else 
        console.log("Your browser does not support File API");
    

JSFiddle:http://jsfiddle.net/Hybridx24/yfr57u6w/

【问题讨论】:

【参考方案1】:

代码的问题在于,当加载事件执行时,for 循环已经递增。因此,如果添加了两个图像 - 执行加载事件时 i 的值已经是 2。

解决此问题的一种方法是将i 的值添加到数组中,并在事件侦听器中一一检索:

var arrFilesCount = [];

for (var i = 0; i < files.length; i++) 
     arrFilesCount.push(i);   //push to array

     var file = files[i];

     //Only pics
     if (!file.type.match('image')) continue;

        var picReader = new FileReader();
        picReader.addEventListener("load", function (event) 
        var picFile = event.target;

        current_i = arrFilesCount.shift(); // get from array instead of using i
        prev_i = current_i - 1;
        next_i = current_i + 1;
        ...
        ...

对应jsFiddlehere


现在,此数组还可用于确定第一个/最后一个元素,从而使用它从最后一个元素转到第一个元素。因为我们无法确定事件侦听器何时执行(例如,如果有 100 张图像,第一个事件侦听器可能会在循环计数达到 5 或 10 时执行),所以我使用了两个循环而不是一个。第一个循环只是为了填充数组。

var arrFilesCount = [];
for (var i = 0; i < files.length; i++) 
     arrFilesCount.push(i);

让我们用它来查找第一个和最后一个元素

current_i = arrFilesCount.shift();
if(current_i === 0)
    prev_i = files.length - 1;   //This is for the first element. The previous slide will be the last image. (i=length-1)

else
    prev_i = current_i - 1;

if(arrFilesCount.length === 0)
    next_i = 0;     //This is for the last element. The next slide will be the first image (i=0)

else
    next_i = current_i + 1;

看到这个jsFiddle。


最后,可能存在用户首先添加几张图片然后再次单击上传按钮并添加更多图片的情况。在这种情况下,我们需要更正现有的 href。我们需要纠正的元素是 last 的 next 和 first 的 prev。这可以使用:

var start = $(output).find('li').length;
var end = start+ files.length;

if(start !== 0)
    $(output).find('li > nav > a.prev').first().attr('href','#slide-' + (end-1));
    $(output).find('li > nav > a.next').last().attr('href','#slide-'+start);

所以最终的 jsFiddle 将类似于 this。

【讨论】:

太棒了,谢谢!不过,我还有一个问题 - 是否有一种特定的方法可以在创建第一张幻灯片时对其进行操作?我尝试使用 jQuery 更改.frame 的高度,但由于某种原因,如果我将代码放在picReader.readAsDataURL(file); 之后,则选择$('#slide-1').find('img').innerHeight() 会返回undefined $('#slide-1') 仅在图像加载并添加到 dom 后可用。所以它只有在 output.innerHtml 执行后才可用。 这就是奇怪的事情——即使我将代码放在输出之后,它仍然不会让我选择#slide-1。我什至尝试将代码放在handleFileSelect(); 之后。有什么想法吗? 原因是一样的:- 图片只有在加载后才会被添加,这可能需要一些时间。到加载事件触发时,其余代码已经执行。请参阅此jsFiddle。这里我添加了两个 console.log 函数。加载图像并查看控制台。注意$('#_imagesInput').on('change' 中写入的第一个日志是如何首先执行的【参考方案2】:

.append() 替换.innerHTML ;创建变量 idx 以增加 .slide li 元素 ids ;将委派的click 事件添加到nav a 元素;添加了.bind(),其中this 设置为picReaderi 作为参数传递给picReaderonload 事件;将file.name 添加到img 元素的title 属性;在#frames 下方添加了带有图像缩略图的“点”导航; title 箭头导航

var idx = -1, re = /(.*)(?=\.)/;

    $('#_uploadImages').click(function() 
      $('#_imagesInput').click();
    );

    $('#_imagesInput').on('change', function(event) 
      handleFileSelect(event);
    );

    $(document).on("click", ".slider .slide nav a, .nav a", function(e) 
      e.preventDefault();
        $(".slide").hide()
        .filter(":has(img[title^="+e.target.title.match(re)[0]+"])").show();
    );

    function handleFileSelect(event) 
      //Check File API support
      if (window.File && window.FileList && window.FileReader) 

        var files = event.target.files; //FileList object
        var output = document.getElementById("frames");

        for (var i = 0; i < files.length; i++) 
          var file = files[i];

          var picReader = new FileReader();
          picReader.onload = function(index, event) 
            ++idx;
            var picFile = event.target;
            var slides = $(".slider li[id^=slide]");
            // TODO: Enter Title
            $(output)
              .append('<li id="slide-' 
                + idx 
                + '" class="slide">' 
                + "<img src='" 
                + picFile.result
                // set `title`
                + "'title="
                //`index` : `i`
                + files[index].name 
                + "/>" 
                + '<nav>' 
                + '<a class="prev">&larr;</a>' 
                + '<a class="next">&rarr;</a>' 
                + '</nav>' 
                + '</li>');
            // add title to `nav a` elements
            if (file.name === files[files.length - 1].name) 
              $(".nav").empty();
              $("nav a").each(function(i, el) 
                if ($(el).closest("[id^=slide]").prev("[id^=slide]").length 
                    && $(el).is("nav a:nth-of-type(1)")) 
                      $(el).attr("title", 
                        $(el).closest("[id^=slide]")
                        .prev("[id^=slide]").find("img").attr("title")
                      )
                

                if ($(el).closest("[id^=slide]").next("[id^=slide]").length 
                    && $(el).is("nav a:nth-of-type(2)")) 
                      $(el).attr("title", 
                        $(el).closest("[id^=slide]")
                        .next("[id^=slide]").find("img").attr("title")
                      )
                

                if ($(el).is(".slider [id^=slide]:first a:first")) 
                  $(el).attr("title", 
                    $("[id^=slide]:last").find("img").attr("title")
                  )
                

                if ($(el).is(".slider [id^=slide]:last a:last")) 
                  $(el).attr("title", 
                    $("[id^=slide]:first").find("img").attr("title")
                  )
                ;
              );
              
              $(".slider img").each(function(i, el) 
                 $(".nav").append(
                   $("nav a[title^="
                     +$(el).attr("title").match(re)[0]
                     +"]:first")
                     .clone().html(el.outerHTML)
                 )
              )
            
          .bind(picReader, i);

          //Read the image
          picReader.readAsDataURL(file);
        ;

       else 
        console.log("Your browser does not support File API");
      
    
* 
  margin: 0;
  padding: 0;
  /*transition*/
  -webkit-transition: all 1s ease;
  -moz-transition: all 1s ease;
  -o-transition: all 1s ease;
  transition: all 1s ease;

body 
  padding: 30px;

/* Slider */

.slider 
  height: 250px;
  left: 50%;
  margin: -125px -225px;
  position: absolute;
  top: 48%;
  width: 450px;
  /*box-shadow*/
  -webkit-box-shadow: 0 0 5px #000;
  -moz-box-shadow: 0 0 5px #000;
  box-shadow: 0 0 5px #000;

.slider .frames 
  height: 250px;
  position: relative;
  list-style-type: none;

.slider .frames .slide 
  height: 250px;
  list-style: none;
  position: absolute;
  width: 450px;

.slider .slide:target 
  z-index: 100

.slider .frames .slide img 
  height: 250px;
  width: 450px;

.slider .frames .slide nav a 
  background: hsla(0, 0%, 0%, .75);
  color: #fff;
  font-size: 16px;
  line-height: 50px;
  margin-top: -25px;
  opacity: 0;
  position: absolute;
  text-align: center;
  text-decoration: none;
  top: 50%;
  width: 50px;
  visibility: hidden;
  z-index: 10;

.slider:hover .frames .slide nav a 
  opacity: 1;
  visibility: visible;

.slider .slide nav a:hover 
  cursor: pointer;

.slider .frames .slide nav .prev 
  /*border-radius*/
  -webkit-border-radius: 0 25px 25px 0;
  -moz-border-radius: 0 25px 25px 0;
  border-radius: 0 25px 25px 0;
  left: 0;

.slider .frames .slide nav .next 
  /*border-radius*/
  -webkit-border-radius: 25px 0 0 25px;
  -moz-border-radius: 25px 0 0 25px;
  border-radius: 25px 0 0 25px;
  right: 0;

.slider .frames .slide nav a:hover 
  background: #000

.slider .quicknav 
  bottom: 0;
  font-size: 0;
  opacity: 0;
  position: absolute;
  text-align: center;
  width: 100%;
  z-index: 100;

.slider:hover .quicknav 
  opacity: .9

.slider .quicknav li 
  display: inline-block

.slider .quicknav a 
  background: hsla(0, 0%, 100%, .9);
  border: 1px solid hsla(0, 0%, 0%, .9);
  /*border-radius*/
  -webkit-border-radius: 100%;
  -moz-border-radius: 100%;
  border-radius: 100%;
  display: block;
  height: 10px;
  margin: 10px 5px;
  text-decoration: none;
  width: 10px;

.slider .quicknav a:hover 
  background: hsla(0, 0%, 50%, .9)


.nav 
 width:100%;
 text-align:center;


.nav a 
  display:inline-block;
  background:transparent;
  border-radius:50%;
  border:4px solid transparent;
  width:24px;
  height:24px;
  margin:4px;


.nav a img 
  width:22px;
  height:22px;
  border-radius:50%;



.slider #one:target ~ .quicknav a[href="#one"],
.slider #two:target ~ .quicknav a[href="#two"],
.slider #three:target ~ .quicknav a[href="#three"],
.slider #four:target ~ .quicknav a[href="#four"],
.slider #five:target ~ .quicknav a[href="#five"] 
  background: hsla(0, 0%, 0%, .9);
  border-color: hsla(0, 0%, 100%, .9);
  background: rgb(244, 246, 245);
  /*linear-gradient*/
  background: -webkit-gradient(linear, left top, left bottom, color-stop(rgba(244, 246, 245, 1), 0.01), color-stop(rgba(203, 219, 219, 1), 1), color-stop(rgba(216, 216, 216, 1), 1));
  background: -webkit-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -moz-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -o-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -webkit-gradient(linear, left top, left bottom, color-stop(1%, rgba(244, 246, 245, 1)), color-stop(100%, rgba(203, 219, 219, 1)), color-stop(100%, rgba(216, 216, 216, 1)));
  background: -webkit-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -moz-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: -o-linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  background: linear-gradient(top, rgba(244, 246, 245, 1) 1%, rgba(203, 219, 219, 1) 100%, rgba(216, 216, 216, 1) 100%);
  filter: progid: DXImageTransform.Microsoft.gradient(startColorstr='#f4f6f5', endColorstr='#d8d8d8', GradientType=0);
  /*box-shadow*/
  -webkit-box-shadow: inset 0 0 3px #000, 0 0 2px rgba(0, 0, 0, .5), 0 2px 3px #666;
  -moz-box-shadow: inset 0 0 3px #000, 0 0 2px rgba(0, 0, 0, .5), 0 2px 3px #666;
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css" rel="stylesheet" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<button id="_uploadImages" class="btn btn-primary">Upload Images</button>

<form id="_imagesForm" action="" method="post">
  <input id="_imagesInput" accept="image/*" type="file" style="display:none" multiple>
</form>

<div id="_displayImages">
  <div class="slider">
    <ul id="frames" class="frames">

    </ul>
    <div class="nav"></div>
  </div>
</div>

jsfiddle http://jsfiddle.net/yfr57u6w/24/

【讨论】:

虽然上面的答案更紧凑,@Taleeb 首先回答,但这是一个很好的选择。我特别喜欢你制作的自定义快速导航缩略图!点赞! @Hybrid 缩略图在连续上传多张图片时尚未返回预期结果。 TODO: 调整.each() ,添加替代方法,以确保.slides 中的每个图像都有.nav a 缩略图。 我明白了......但无论如何,这是一个极好的设计步骤!我会看看我是否可以进一步整合它。

以上是关于Slider FileReader JS 多张图片上传(递增索引)的主要内容,如果未能解决你的问题,请参考以下文章

js上传图片&预览(filereader)

Js FileReader图片加载

js的FileReader实现图片文件上传预览

3分钟上手JS中的FileReader对象(实现上传图片预览)

怎样用js插入多张图片

js通过fileReader获取图片的base64格式