交互式树屋广告的制作

Avatar of Chris Coyier
Chris Coyier

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

访问此网站的人可能很清楚: Treehouse 是我们主要的赞助商。 我喜欢这一点。 不仅因为他们提供结构化的学习,与我在这里提供的学习资料相辅相成,而且因为拥有一个主要的赞助商意味着我们可以花更多时间以有趣的方式将广告整合到网站中。

在与 Treehouse 的各位交谈时,他们告诉我他们有一些值得推广的新 jQuery 内容。 我想创建一个小的交互式区域,让大家对 jQuery 的功能有所了解,并激发他们学习更多关于它的兴趣。

让我们来看看它是如何构建的。

想法

  1. 有一个“画布”(不是 <canvas>,只是一个发生事情的区域)
  2. 有一系列五个简单的 jQuery 代码片段
  3. 单击按钮,代码运行,在画布上发生一些事情
  4. 您可以看到将要执行的代码
  5. 它应该直观地表明您看到的代码正在运行并进行更改
  6. 一直以来,画布区域都是 Treehouse 的可点击广告(用户不需要与 jQuery 内容交互,它仍然只是一个广告)
  7. 完成后,您可以从交互步骤重新开始

以下是最终的结果。 演示 在 CodePen 上

HTML 代码

相关部分已添加注释

<div class="ad">
   
  <!-- Our jQuery code will run on elements in here -->
  <div class="canvas" id="canvas">
    <a id="treehouse-ad" href="http://teamtreehouse.com" target="_blank">Team Treehouse!</a>
    <img src="//css-tricks.cn/wp-content/themes/CSS-Tricks-10/images/mike-standard.png" alt="Mike">
  </div>
  
  <div id="code-area-wrap">

    <h3>jQuery 
      <!-- We'll increment the "step", so we need an element to target -->
      <span id="step-counter">1/5</span>
    </h3>

    <!-- Code is visually displayed in here -->
    <pre class="code-area" id="code-area">var treehouse = 
$('#treehouse-ad')
  .addClass('button');</pre>

  </div>
  
  <div class="run-code-wrap">

    <!-- This will be pressed to run the code -->
    <a href="#" class="button" id="run-code">Run Code</a>

  </div>
  
</div>

CSS 代码

这里没什么有趣的,所以我们不要过多关注它。 本质上,我让广告重复使用 CSS-Tricks 上已经使用的样式,因此广告看起来很和谐。 代码块使用与网站其他地方相同的代码样式。 模块顶部有一个条形,就像所有其他模块一样。 按钮是全局按钮样式。 就是这样。

我在“画布”周围放置了一个虚线边框,我认为这会给它一种可编辑/目标的感觉。

JavaScript 代码

现在我们需要让它工作。 这意味着……

  1. 单击按钮时……
  2. 执行代码
  3. 递增步骤
  4. 如果这是最后一步,则设置重新开始的步骤

结构

我们编写的 JavaScript 代码本质上是一个“模块”。 它们彼此相关。 我们应该赋予它一些结构,使其一目了然。 我知道的最好的方法是创建一个包含所有内容的对象。

var TreehouseAd = {

  // Everything related to this ad goes in here.

};

这通常会得到类似这样的结果

var TreehouseAd = {

  importantVariableOne: "foo",
  importantVariableTwo: "bar",

  init: function() {
    // The one function that will get called to kick things off

    this.bindUIActions();
  },

  bindUIActions: function() {
    // bind events here
    // have them call appropriately named functions
    
    $("button").on("click", function(event) {
       TreehouseAd.doSomething(event, this);
    });
  },

  doSomething: function(event, target) {
    // do something
  }

};

TreehouseAd.init();

使用这种结构可以让代码更易读、更易于测试,而且总体感觉比一堆零散的函数和变量(意大利面条代码)要好。

“步骤”

我们将有五个步骤。 就像我们针对广告画布执行的不同 jQuery 代码片段。

  1. 将“按钮”类添加到链接
  2. 向上移动一点(演示动画)
  3. 更改按钮的文字
  4. 添加一个新元素
  5. 将类添加到整个广告中,使其更加完善

我们还需要对每个代码片段做两件事

  1. 显示它
  2. 执行它

我们最好不要在两个不同的位置维护代码,所以我们不会这样做。 我们将五个不同的代码片段存储在数组中(赋予它一些结构,而不是五个单独的变量)。 数组中的每个项目都是一个代码字符串。 我们可以获取该字符串并显示它,或者获取该字符串并使用 eval() 执行它。

我知道。 eval() 很邪恶。 我们可以通过在不同位置维护代码示例来避免它。 或者有更好的想法吗?
var TreehouseAd = {

   ...

   codeText: [

      "code for step 1",
      "code for step 2",
      ...
   ],

   ...

}

最终得到这样的结果

codeText: [

  "treehouse = \n\
  $('#treehouse-ad')\n\
    .addClass('button');",
  
  "treehouse.animate({\n\
  top: '-40px'\n\
});",
  
  "treehouse.text(\n\
  'Learn jQuery at Treehouse!'\n\
);",
  
  "$('<div />', {\n\
  id: 'tagline',\n\
  text: "Hi, I'm Mike."\n\
}).insertAfter(treehouse);",
  
  "$('#canvas')\n\
 .addClass('all-done');\n\n\
console.log('Thanks for playing!')" 
  
],

注意到行末的所有斜杠和“n”吗? 在 JavaScript 中,以“\”结尾的字符串只是意味着“此字符串将在下一行继续”。 它不会出现在字符串本身中。 “\n”表示“换行”,在本例中,我们需要它,以便在将其放置在 <pre> 元素中时代码能够正确格式化。 它不会影响 eval()

动画

为了直观地显示按下“运行代码”按钮时发生了什么,我认为让代码向上滑动并淡入“画布”将是一个很酷的想法。 因此,步骤变为

  1. 在现有代码区域的正上方创建一个克隆/复制代码区域
  2. 淡出时向上移动动画位置
  3. 完成后移除克隆
  4. 等待动画完成再更改代码文本

我认为此操作是一个“块”功能。 这意味着它应该作为我们对象的一部分拥有自己的函数。

var TreehouseAd = {

  ...

  codeArea: $("#code-area"),
  delay: 400,

  animateCode: function() {

   this.codeArea
    .clone()
    .addClass("clone")
    .insertAfter(this.codeArea)
    .animate({
      top: "-60px",
      opacity: 0
    }, TreehouseAd.delay, function() {
      $(".clone").remove(); 
    });

  },

  ...

};

核心

最终代码如下所示。 其中包含了运行代码、重置以及所有其他逻辑。

var TreehouseAd = {
  
  step: 0,
  delay: 400,
  codeArea: $("#code-area"),
  counter: $("#step-counter"),

  init: function() {
    this.bindUIActions();
  },
  
  codeText: [
  
    "treehouse = \n\
    $('#treehouse-ad')\n\
      .addClass('button');",
    
    "treehouse.animate({\n\
    top: '-40px'\n\
  });",
    
    "treehouse.text(\n\
    'Learn jQuery at Treehouse!'\n\
  );",
    
    "$('<div />', {\n\
    id: 'tagline',\n\
    text: "Hi, I'm Mike."\n\
  }).insertAfter(treehouse);",
    
    "$('#canvas')\n\
   .addClass('all-done');\n\n\
  console.log('Thanks for playing!')" 
    
  ],
  
  bindUIActions: function() {
    
    $("#run-code").on("click", function(e) {
      e.preventDefault();
      TreehouseAd.animateCode();
      TreehouseAd.runCode();
    });
                      
  },
                      
  animateCode: function() {
      
   this.codeArea
    .clone()
    .addClass("clone")
    .insertAfter(this.codeArea)
    .animate({
      top: "-60px",
      opacity: 0
    }, TreehouseAd.delay, function() {
      $(".clone").remove(); 
    });
      
  },
    
  runCode: function() {
    
    setTimeout(function() {
  
      if (TreehouseAd.step < 5) {
         
        eval(TreehouseAd.codeText[TreehouseAd.step]);
        
        TreehouseAd.step++;
        
        TreehouseAd.counter.text((TreehouseAd.step+1) + "/5");
        TreehouseAd.codeArea.text(TreehouseAd.codeText[TreehouseAd.step]);
                
      }
      
      if (TreehouseAd.step == 6) {
          
        // reset canvas
        treehouse
          .text("Team Treehouse!")
          .removeClass()
          .removeAttr("style");
        
        $("#tagline").remove();
        $("#canvas").removeClass("all-done");
        $("#run-code").text("Run Code");
        
        TreehouseAd.step = 0;
        TreehouseAd.codeArea.text(TreehouseAd.codeText[TreehouseAd.step]);
        TreehouseAd.counter.text((TreehouseAd.step+1) + "/5");
        
      }
      
      if (TreehouseAd.step == 5) {
        
        $("#run-code").text("Start Over");
        TreehouseAd.codeArea.text("");
        TreehouseAd.counter.text("Done!");
        
        TreehouseAd.step++;
        
      }
    
    }, TreehouseAd.delay);
    
  }
  
}; 

TreehouseAd.init();

结束

同样,CodePen 上的演示

您也可以在这个网站上看到它的修改版本! 我们已经为乐趣而修改过它几次。 可能会继续这样做。 非常感谢 Treehouse 让我做这些有趣的事情。 如果您是 CSS-Tricks 的粉丝,请查看他们的网站。