使用 PHP 和 JavaScript 的幻灯片放映

Slideshow using PHP and JavaScript

我一直在尝试主要使用 PHP 和 JavaScript 创建幻灯片。但是,由于某种原因,在我单击 >><< 移动到下一张或上一张幻灯片后没有任何响应。

PHP脚本主要用于从文件夹中抓取图片并组成数组。然后我通过 echo 在 PHP 中使用 JavaScript 将 PHP-array 转换为 JavaScript-array.

我用 this website 作为教程。

感谢您的帮助!

<?php
//PHP SCRIPT: getimages.php
//Header("content-type: text/javascript");

//This function gets the file names of all images in the current directory
//and ouputs them as a JavaScript array
function returnimages($dirname='C:\Apache\htdocs\Psych211LifespanDevchpt1') {
    echo "in return images";
    $files = array();
    $curimage = 0;

    if ($handles = opendir($dirname)) {
        echo "in handles";
        while (false !== ($file = readdir($handles))) {
            echo "in while";
            $path_info = $_FILES['file']['name'];
            $extensions = pathinfo($path_info, PATHINFO_EXTENSION);
            echo $extensions;
            if ($extensions=='png')
            //if(eregi($pattern, $file)) { //if this file is a valid image
                //Output it as a JavaScript array element
                //$files->append($file);
                $files[$curimage] = $file;
                array_push($files,$file);
                //echo $files[$curimage];
                // 'Slides['.$curimage.']="'.$file .'";';
                //echo $curimage;
                $curimage++;
            //}
        }
        echo $curimage;
        echo count($files);
        closedir($handle);
    }
    echo '<html>
    <head>
    <script type="text/javascript"> 
    var Slides = [];
    Slides = '.json_encode($files).'; document.write(Slides.length);
    function CacheImage(ImageSource) { // TURNS THE STRING INTO AN IMAGE OBJECT
        var ImageObject = new Image();
        ImageObject.src = ImageSource;
        return ImageObject;
    }

    function ShowSlide(Direction) {
       if (SlideReady) {
          NextSlide = CurrentSlide + Direction;
          // THIS WILL DISABLE THE BUTTONS (IE-ONLY)
          document.SlideShow.Previous.disabled = (NextSlide == 0);
          document.SlideShow.Next.disabled = (NextSlide == 
    (Slides.length-1));    
     if ((NextSlide >= 0) && (NextSlide < Slides.length)) {
                document.images["Screen"].src = Slides[NextSlide].src;
                CurrentSlide = NextSlide++;
                Message = "Picture " + (CurrentSlide+1) + "of " + 
    Slides.length;
                self.defaultStatus = Message;
                if (Direction == 1) CacheNextSlide();
          }
          return true;
       }
    }

    function Download() {
       if (Slides[NextSlide].complete) {
          SlideReady = true;
          self.defaultStatus = Message;
       }
       else setTimeout("Download()", 100); // CHECKS DOWNLOAD STATUS EVERY 100 MS
       return true;
    }

    function CacheNextSlide() {
       if ((NextSlide < Slides.length) && (typeof Slides[NextSlide] == 
    "string"))
    { // ONLY CACHES THE IMAGES ONCE
          SlideReady = false;
          self.defaultStatus = "Downloading next picture...";
          Slides[NextSlide] = CacheImage(Slides[NextSlide]);
          Download();
       }
       return true;
    }

    function StartSlideShow() {
       CurrentSlide = -1;
       Slides[0] = CacheImage(Slides[0]);
       var SlideReady = true;
       ShowSlide(1);
    }

    </script>
    </head>
    <body onLoad="StartSlideShow()"> 
<form name="SlideShow"> 
<table> 
<tr> 
<td colspan=2><img src="Psych211LifespanDevchpt1/slide-1.png" name="Screen" width=108 height=135></td>
 </tr>
 <tr> 
<td><input type="button" name="Previous" value=" << " onClick="ShowSlide(-1)"></td> 
<td align="right"><input type="button" name="Next" value=" >> " onClick="ShowSlide(1)"></td> 
</table> 
</form> 
</body> 
</html>';

//return($files);


    }

    //echo 'var Slides=new Array();'; //Define array in JavaScript
    returnimages() //Output the array elements containing the image file names
    ?>

您的大多数函数都使用 implicit global 变量名 SlideReady。在 StartSlideShow 你用 var SlideReady = true; 试试看设置。因为 你在前面加上 var 你正在设置一个也被命名的局部变量 SlideReady,不是其他函数可以访问的全局变量,所以 全局 SlideReady 仍然是 undefined.

所以当 StartSlideShow 调用 ShowSlide 时,行

if (SlideReady) {

被解释为

if (false) {

因为全局SlideReadyundefined,这是一个falsy值。 if 语句中的假值被强制为 false,因此 if 块中的代码永远不会 运行s 并且幻灯片永远不会更改。

使用像 jsHint or the even stricter jsLint will help you avoid these kinds of errors. Running all your code in strict mode 这样的 linter 也会使这种错误更加明显。此代码中还有许多其他隐式全局变量(NextSlideCurrentSlideMessage)。隐式全局变量可能导致这样的错误甚至更隐蔽的错误。

其他可以改进的地方

window.self isn't commonly used. In particular the way this code uses it as a bare self without prefacing it with window could cause headaches. Many people use a variable named self to store a reference to this in situations where they need to access a this object of an outer function from inside another function using closure。使用 self 来引用 window.self 可能会使任何阅读使用它的代码的人感到困惑。 (它对我有用,我已经很久没有看到对 window.self 的引用了,我忘了它的存在!)

window.defaultStatus 已经过时很久了,大多数浏览器甚至都没有状态栏了;没有人会看到发布在那里的消息。

这段代码有很多大写的变量名。它是有效的代码并且将 运行,但在 JavaScript 中它已成为一种约定,只有 constructor functions 被大写。用最简单的术语来说,构造函数是需要使用 new 关键字调用的函数。如果将来有人(包括你)处理代码,函数名被大写是提醒你需要在它前面使用 new 关键字。 Linters 也使用此约定自动为您发现丢失的 news。

通过 document.formname.elementName 访问表单元素和通过 document.images 访问图像是非常过时的技术。使用document.formName.elementName也可以cause problems if a form element has the same name as a property of the form. Instead giving each button an idand accessing it via document.getElementById。事实上,您甚至不需要使用表单和按钮,将 divspan 元素设置为看起来像按钮的样式很容易。

使用 onclick 是一种过时的附加事件方式,尤其是将其嵌入到 HTML 的 onclick 中。反而少了obtrusive to use addEventListener

For a number of reasons many people writing modern JavaScript prefer to use function expressions to define functions instead of function declaration。函数声明可能会导致一些令人费解的错误,而存储在变量中的函数表达式的行为更可预测。

全局变量不好,经常会导致各种奇怪的错误,但通常代码的不同部分需要访问公共变量。您可以通过将代码包装在 Immediately-Invoked Function Expression (IIFE)(有时也称为自调用函数)中来实现这一点,而无需创建全局变量。

(function () {
  'use strict';
  var shared = true,
    foo = function () {
      // has access to shared
    },
    bar = function () {
      // also has access to shared
    };
// you can access shared here
}());
// you can't access shared here because it is not a global

我个人觉得没有必要也不值得预加载图片。大多数人的连接速度相当快,如果图像编码正确,它们将逐步加载,并且在某些状态下可以比使用此代码在完全加载之前不显示图像的情况下更快地查看。在用户请求之前加载下一张图片也会浪费他们的带宽,如果他们在移动设备上,这可能很重要。

如果你认为预加载图片很重要,与其使用过于复杂的系统,直到完全下载才显示下一张图片,我会在开始时预加载它们:

Slides.forEach(function (url) {
  (new Image()).src = url;
});

这将下载每个图像并将其存储在浏览器缓存中,而不是像您发布的代码那样将其保存在内存中的数组中。

使代码现代化

该教程中 JavaScript 代码的更现代版本可能看起来更像这样:

<img id="slide"></td>
<div class="slide-controls">
  <span class="button" id="prev-slide">&laquo;</span>
  <span class="button" id="next-slide">&raquo;</span>
</div>

<script>
  (function () {
    'use strict';
    var slides = [
        'http://www.placecage.com/300/200',
        'http://www.placecage.com/g/300/200',
        'http://www.placecage.com/c/300/200',
        'http://www.placecage.com/gif/300/200'
      ],
      currentSlide = 0,
      doc = document,
      elSlide = doc.getElementById('slide'),
      elPrev = doc.getElementById('prev-slide'),
      elNext = doc.getElementById('next-slide'),

      showSlide = function (index) {
        if (index > -1 && index < slides.length) {
          currentSlide = index;
          elPrev.classList.remove('disabled');
          elNext.classList.remove('disabled');
          if (index === 0) {
            elPrev.classList.add('disabled');
          } else if (index === slides.length - 1) {
            elNext.classList.add('disabled');
          }
          elSlide.src = slides[index];
          elSlide.title = 'Picture ' + (index + 1) + 'of ' + slides.length;
        }
      },
      changeSlide = function (step) {
          var index = currentSlide + step;
          showSlide(index);
      },
      prevSlide = changeSlide.bind(null, -1),
      nextSlide = changeSlide.bind(null, 1);

    elPrev.addEventListener('click', prevSlide, false);
    elNext.addEventListener('click', nextSlide, false);

    showSlide(0);
  }());
</script>

jsFiddle

不需要使用docuemntload event (aka onLoad),元素在JavaScript代码上方的标记中定义,当JavaScript代码时可用运行s.

我没有使用 defaultStatus,而是将状态消息放在图像的 title 属性中,如果用户将鼠标悬停在上方,它应该在大多数浏览器中显示为工具提示图片。如果您希望在没有鼠标悬停的情况下看到它,您也可以将消息输出到另一个 <div>

我使用 Function.prototype.bind 创建事件处理器 prevSlidenextSlide

线条

  prevSlide = changeSlide.bind(null, -1),
  nextSlide = changeSlide.bind(null, 1);

实际上等于

  prevSlide = function () {
    changeSlide(-1);
  },
  nextSlide = function () {
    changeSlide(1);
  };

由于我将 changeSlide 逻辑与图像的实际显示分开,并且 showSlide 采用索引而不是前进或后退的次数,因此很容易制作一个函数也跳转到第一张或最后一张幻灯片:

  var firstSlide = showSlide.bind(null, 0),
    lastSlide = showSlide.bind(null, slides.length - 1);

jsFiddle

其他资源

这里有一些与我使用的技术相关的资源。