2009-05-01 2 views
7

jquery's accordion plugin과 비슷한 jquery에서 아코디언 위젯을 만들려고합니다. 차이점은 핸들을 위의 내용 대신에 아래에 표시하고 싶습니다. 내 아코디언은 열린 콘텐츠 섹션의 높이를 낮추는 동시에 클릭 한 콘텐츠 섹션의 높이를 높이는 방식으로 작동합니다. 예 : here을 게시했습니다. 내 문제는 애니메이션이 정확히 같은 시간에 시작되지 않으며 두 번째 애니메이션이 시작되기 전에 약간의 지연으로 인해 눈에 띄는 "점프"가 있다는 것입니다.jquery가 정확히 병렬로 애니메이션을 실행하도록하려면 어떻게해야합니까?

Scriptaculous에는 Effect.Parallel이라는 기능이있어 애니메이션 효과의 배열을 만들고이를 병렬로 실행할 수 있습니다. 불행히도 jquery와 비슷한 것을 찾을 수 없습니다.

jquery의 별도 div에서 정확한 병렬 애니메이션을 실행할 수있는 방법이 있습니까?

편집 :이 아코디언 위젯을 코딩하는 대체 방법에 관심이 많습니다. 그래서 사람들이 일할 것이라고 생각하는 다른 방법이 있다면 나는 그것에 대해 개방적입니다.

+0

SGIL에서이 작업을 수행 할 수는 있지만 마크 업 언어와 약간 다른 주제입니다. –

+0

내 최종 솔루션을 참조하십시오 : http://stackoverflow.com/questions/811750/how-can-i-get-jquery-to-execute-animations-in-exact-parallel/835362#835362 –

답변

4

하나 더 대답, 희망 내 마지막 ... 불행하게도

, 존 레식의 syncAnimate의 방법은 아코디언 타입의 애니를 위해 스너프까지는 아니야. 나는하고 싶다. Firefox에서는 훌륭하게 작동하지만 IE 나 Safari에서는 원활하게 작동하지 않습니다.

그런 말로, 나는 총알을 물고 간단한 병렬 애니메이션을하는 내 자신의 애니메이션 엔진을 작성하기로 결정했습니다. 클래스 코드는 jquery 함수를 사용하지만 jquery 플러그인은 아닙니다. 또한 크기/위치 애니메이션을 설정하기 만 했으므로 필요한 모든 것입니다.

ParallelAnimations = function(animations, opts){ 
    this.init(animations, opts); 
}; 

$.extend(ParallelAnimations.prototype, { 
    options: { 
     duration: 250 
    }, 
    rules: {}, 

    init: function(animations, opts){ 
     // Overwrite the default options 
     $.extend(this.options, opts); 

     // Create a set of rules to follow in our animation 
     for(var i in animations){ 
      this.rules[i] = { 
       element: animations[i].element, 
       changes: new Array() 
      }; 

      for(var style in animations[i].styles){ 

       // Calculate the start and end point values for the given style change 
       var from = this.parse_style_value(animations[i].element, style, ""); 
       var to = this.parse_style_value(animations[i].element, style, animations[i].styles[style]); 

       this.rules[i].changes.push({ 
        from: from, 
        to: to, 
        style: style 
       }); 
      } 
     } 

     this.start() 
    }, 

    /* 
    * Does some parsing of the given and real style values 
    * Allows for pixel and percentage-based animations 
    */ 
    parse_style_value: function(element, style, given_value){ 
     var real_value = element.css(style); 

     if(given_value.indexOf("px") != -1){ 
      return { 
       amount: given_value.substring(0, (given_value.length - 2)), 
       unit: "px" 
      }; 
     } 

     if(real_value == "auto"){ 
      return { 
       amount: 0, 
       unit: "px" 
      }; 
     } 

     if(given_value.indexOf("%") != -1){ 
      var fraction = given_value.substring(0, given_value.length - 1)/100; 

      return { 
       amount: (real_value.substring(0, real_value.length - 2) * fraction), 
       unit: "px" 
      }; 
     } 

     if(!given_value){ 
      return { 
       amount: real_value.substring(0, real_value.length - 2), 
       unit: "px" 
      }; 
     } 
    }, 

    /* 
    * Start the animation 
    */ 
    start: function(){ 
     var self = this; 
     var start_time = new Date().getTime(); 
     var freq = (1/this.options.duration); 

     var interval = setInterval(function(){ 
      var elapsed_time = new Date().getTime() - start_time; 

      if(elapsed_time < self.options.duration){ 
       var f = elapsed_time * freq; 

       for(var i in self.rules){ 
        for(var j in self.rules[i].changes){ 
         self.step(self.rules[i].element, self.rules[i].changes[j], f); 
        } 
       } 
      } 
      else{ 
       clearInterval(interval); 

       for(var i in self.rules){ 
        for(var j in self.rules[i].changes) 
         self.step(self.rules[i].element, self.rules[i].changes[j], 1); 
       } 
      } 
     }, 10); 
    }, 

    /* 
    * Perform an animation step 
    * Only works with position-based animations 
    */ 
    step: function(element, change, fraction){ 

     var new_value; 
     switch(change.style){ 
      case 'height': 
      case 'width': 
      case 'top': 
      case 'bottom': 
      case 'left': 
      case 'right': 
      case 'marginTop': 
      case 'marginBottom': 
      case 'marginLeft': 
      case 'marginRight': 
       new_value = Math.round(change.from.amount - (fraction * (change.from.amount - change.to.amount))) + change.to.unit; 
       break; 
     } 

     if(new_value) 
      element.css(change.style, new_value); 
    } 
}); 

원래 Accordion 클래스는 새 호출을 사용하기 위해 animate 메서드에서만 수정해야합니다. 다른 스타일의 유형 (색상에 대한 애니메이션 - 대기중인 애니메이션 - :

<html> 
<head> 
    <title>Parallel Accordion Animation</title> 
    <script type="text/javascript" src="jquery.js"></script> 
    <script type="text/javascript" src="ui.js"></script> 
    <script type="text/javascript"> 
    $(document).ready(function(){ 
     new Accordion("#accordion"); 
    }); 
    </script> 
    <style type="text/css"> 
    #accordion{ 
     position: relative; 
    } 
    #accordion .handle{ 
     width: 260px; 
     height: 30px; 
     background-color: orange; 
    } 
    #accordion .section{ 
     width: 260px; 
     height: 445px; 
     background-color: #a9a9a9; 
     overflow: hidden; 
     position: relative; 
    } 
    </style> 
</head> 
<body> 

<div id="accordion"> 
    <div class="section"><!-- --></div> 
    <div class="handle">handle 1</div> 
    <div class="section"><!-- --></div> 
    <div class="handle">handle 2</div> 
    <div class="section"><!-- --></div> 
    <div class="handle">handle 3</div> 
    <div class="section"><!-- --></div> 
    <div class="handle">handle 4</div> 
    <div class="section"><!-- --></div> 
    <div class="handle">handle 5</div> 
</div> 

</body> 
</html> 

내가 미래에 추가 할 수있는 몇 가지가 있습니다

Accordion = function(container_id, options){ 
    this.init(container_id, options); 
} 

$.extend(Accordion.prototype, { 
    container_id: '', 
    options: {}, 
    active_tab: 0,  
    animating: false, 
    button_position: 'below', 
    duration: 250, 
    height: 100, 

    handle_class: ".handle", 
    section_class: ".section", 

    init: function(container_id, options){ 
     var self = this; 
     this.container_id = container_id; 
     this.button_position = this.get_button_position(); 

     // The height of each section, use the height specified in the stylesheet if possible 
     this.height = $(this.container_id + " " + this.section_class).css("height"); 

     if(options && options.duration) this.duration = options.duration; 
     if(options && options.active_tab) this.active_tab = options.active_tab; 

     // Set the first section to have a height and be "open" 
     // All the rest of the sections should have 0px height 
     $(this.container_id).children(this.section_class).eq(this.active_tab) 
      .addClass("open") 
      .css("height", this.height) 
      .siblings(this.section_class) 
      .css("height", "0px"); 

     // figure out the state of the handles 
     this.do_handle_logic($(this.container_id).children(this.handle_class).eq(this.active_tab)); 

     // Set up an event handler to animate each section 
     $(this.container_id + " " + this.handle_class).mouseover(function(){ 

      if(self.animating) 
       return; 

      self.animate($(this)); 
     }); 
    }, 

    /* 
    * Determines whether handles are above or below their associated section 
    */  
    get_button_position: function(){ 
     return ($(this.container_id).children(":first").hasClass(this.handle_class) ? 'above' : 'below'); 
    }, 

    /* 
    * Animate the accordion from one node to another 
    */ 
    animate: function(handle){ 
     var active_section = (this.button_position == 'below' ? handle.prev() : handle.next());  
     var open_section = handle.siblings().andSelf().filter(".open"); 

     if(active_section.hasClass("open")) 
      return; 

     this.animating = true; 

     // figure out the state of the handles 
     this.do_handle_logic(handle); 

     // Close the open section 
     var arr = new Array(); 
     arr.push({ 
      element: open_section, 
      styles: { 
       "height": "0px" 
      } 
     }); 
     arr.push({ 
      element: active_section, 
      styles: { 
       "height": this.height 
      } 
     }); 
     new ParallelAnimations(arr, {duration: this.duration}); 

     var self = this; 
     window.setTimeout(function(){ 
      open_section.removeClass("open"); 
      active_section.addClass("open"); 
      self.animating = false; 
     }, this.duration); 
    }, 

    /* 
    * Update the current class or "state" of each handle 
    */ 
    do_handle_logic: function(handle){ 
     var all_handles = handle.siblings(".handle").andSelf(); 
     var above_handles = handle.prevAll(this.handle_class); 
     var below_handles = handle.nextAll(this.handle_class); 

     // Remove all obsolete handles 
     all_handles 
      .removeClass("handle_on_above") 
      .removeClass("handle_on_below") 
      .removeClass("handle_off_below") 
      .removeClass("handle_off_above"); 

     // Apply the "on" state to the current handle 
     if(this.button_position == 'below'){ 
      handle 
       .addClass("handle_on_below"); 
     } 
     else{ 
      handle 
       .addClass("handle_on_above"); 
     } 

     // Apply the off above/below state to the rest of the handles 
     above_handles 
      .addClass("handle_off_above"); 

     below_handles 
      .addClass("handle_off_below"); 
    } 
}); 

는 HTML 여전히 같은 방식이라고합니다 등)

+0

훨씬 쉬운 방법은 애니메이션에 스텝 기능을 전달하는 것입니다. 자세한 내용은 여기를 참조하십시오. http://docs.jquery.com/Release:jQuery_1.2/Effects#Extensible_Animations – johjoh

3

John Resig는 synchronized animation sample (지침 없음, 색칠 된 상자 클릭)을 게시했습니다. 그것을 당신의 통제에 적용하는 방법을 이해하는 데는 약간의 노력이 필요할 수도 있지만, 시작할 수있는 좋은 장소가 될 수 있습니다.

+0

내 아코디언으로 이것을 비교적 사용하기 쉽고, 정말 잘 작동합니다. 추가 된 이점은 마크 업에서 모든 로직을 지우고 자바 스크립트에서 모두 가질 수 있다는 것입니다. –

0

당신의 문제는 타이밍이 아니라 픽셀의 분수 분할이라고 생각합니다. 이 코드를 사용하면 핸들 1과 2는 부드럽게 보이지만 파이어 폭스 3에서는 보이지 않지만 크롬에서는 여전히 불안해 보인다.

active 
    .animate({ height: "100px" }) 
    .siblings(".section") 
    .animate({ height: "0px" }); 

은 정적 또는 절대 요소의 위치를 ​​만드는 방법에 대한 생각? 두 요소의 위치 만 이동하는 경우 점프하는 다른 요소에 대해 걱정할 필요가 없습니다. 잠시만 기다려주세요. 예를 보여 드리겠습니다.

2

이것은 실행중인 애니메이션을 병렬로 처리하지 않지만 지터없이 예상되는 동작을 재생합니다. 애니메이션의 수를 줄이기 위해 핸들 내부에 섹션을 배치했습니다. andSelf()를 사용하여 코드를 작게 만들 수는 있지만 읽는 것이 더 어려울 수 있습니다. 스타일을 바꿀 필요가 있습니다.

<html> 
<head> 
    <title>Accordion Test</title> 
    <script type="text/javascript" src="jquery.js"></script> 
    <script type="text/javascript"> 

    $(document).ready(function(){ 
     $("#accordion .handle").click(function(){ 
      var open = $(this).parent().children(".section, .open"); 
      var active = $(this); 

      if (!active.hasClass("open")) 
      { 
       if (active.hasClass("up")) 
       { 
        console.log("up"); 
        active.animate({top:"+=100"}).removeClass("up"); 
        active.nextAll(".handle").andSelf().filter(".up").animate({top:"+=100"}).removeClass("up"); 
        $(".section", active).slideUp(); 
        $(".section", active.nextAll()).slideUp(); 
        $(".section", active.prev()).slideDown(); 
       } 
       else 
       { 
        active.prevAll(".handle").not(".up").animate({top:"-=100"}).addClass("up"); 
        $(".section", active.prev()).slideDown(); 
       } 

       open.removeClass("open"); 
       active.addClass("open"); 
      } 
     }); 
    }); 

    </script> 
    <style type="text/css"> 
     #accordion{ 
      width: 200px; 
      position:relative; 
     } 
     #accordion .section{ 
      width: 196px; 
      margin-left: 2px; 
      height: 100px; 
      background-color: #b9b9b9; 
      display:none; 
     } 
     #accordion .handle{ 
      width: 200px; 
      height: 30px; 
      background-color: #d9d9d9; 
      border: 1px solid black; 
      cursor: pointer; 
      cursor: hand; 
      position: absolute; 
     } 
     #accordion .handle .header { 
      height: 30px; 
     } 
    </style> 
</head> 
<body> 

<div id="accordion"> 
    <div id="s1" class="section open" style="display:block">This is section 1</div> 

    <div class="handle open" style="top:100;"> 
     <div class="header">handle 1</div> 
     <div class="section">This is section 2</div> 
    </div> 

    <div class="handle" style="top:130;"> 
     <div class="header">handle 2</div> 
     <div class="section">This is section 3</div> 
    </div> 

    <div class="handle" style="top:160;"> 
     <div class="header">handle 3</div> 
     <div class="section">This is section 4</div> 
    </div> 

    <div class="handle" style="top:190;"> 
     <div class="header">handle 4</div> 
     <div class="section">This is section 5</div> 
    </div> 

    <div class="handle" style="top:220;"> 
     <div class="content">handle 5</div> 
    </div> 
</div> 

</body> 
</html> 
+0

이 코드는 작동합니다! 부드럽고 원치 않는 움직임이 없습니다. –

+0

잠시 동안 놀고 난 후 Corbin March이 게시 한 솔루션을 사용하기로 결정했습니다. –

0

업데이트 : 존 레식의 syncAnimate 플러그인을 사용하여 더 이상 해요. 최종 솔루션에 대한 나중의 대답을 참조하십시오.

저는 제가 프로젝트에서 사용하고있는 최종 작업 솔루션을 제공하기를 원했습니다. John Resig가 작성한 syncAnimate plugin을 사용합니다 (Corbin March 게시).

  • 읽기와 옵션은 객체를 통해
  • 당신이 애니메이션 시간을 설정할 수 있도록 허용 CSS 및 기본 활성 섹션 섹션 높이를 사용

    이 코드는 것이다.

  • 섹션과 관련된 핸들 위치를 자동으로 감지하여 그에 따라 조정합니다. 따라서 마크 업에서 섹션을 위 또는 아래로 이동하고 js 코드를 변경할 필요가 없습니다.

HTML

<script type="text/javascript" src="jquery.js"></script> 
<script type="text/javascript" src="ui.js"></script> 

<script type="text/javascript"> 
$(document).ready(function(){ 
    new Accordion("#accordion", {active_tab: 0}); 
}); 
</script> 
<style type="text/css"> 
#accordion .handle{ 
    width: 260px; 
    height: 30px; 
    background-color: orange; 
} 
#accordion .section{ 
    width: 260px; 
    height: 445px; 
    background-color: #a9a9a9; 
    overflow: hidden; 
    position: relative; 
} 

</style> 

<div id="accordion"> 
    <div class="section">Section Code</div> 
    <div class="handle">handle 1</div> 

    <div class="section">Section Code</div> 
    <div class="handle">handle 2</div> 

    <div class="section">Section Code</div> 
    <div class="handle">handle 3</div> 

    <div class="section">Section Code</div> 
    <div class="handle">handle 4</div> 

    <div class="section">Section Code</div> 
    <div class="handle">handle 5</div> 
</div> 

ui.js

Accordion = function(container_id, options){ 
    this.init(container_id, options); 
} 

$.extend(Accordion.prototype, { 
    container_id: '', 
    options: {}, 
    active_tab: 0,  
    animating: false, 
    button_position: 'below', 
    duration: 250, 
    height: 100, 

    handle_class: ".handle", 
    section_class: ".section", 

    init: function(container_id, options){ 
     var self = this; 
     this.container_id = container_id; 
     this.button_position = this.get_button_position(); 

     // The height of each section, use the height specified in the stylesheet if possible 
     this.height = $(this.container_id + " " + this.section_class).css("height"); 

     if(options && options.duration) this.duration = options.duration; 
     if(options && options.active_tab) this.active_tab = options.active_tab; 

     // Set the first section to have a height and be "open" 
     // All the rest of the sections should have 0px height 
     $(this.container_id).children(this.section_class).eq(this.active_tab) 
      .addClass("open") 
      .css("height", this.height) 
      .siblings(this.section_class) 
      .css("height", "0px"); 

     // figure out the state of the handles 
     this.do_handle_logic($(this.container_id).children(this.handle_class).eq(this.active_tab)); 

     // Set up an event handler to animate each section 
     $(this.container_id + " " + this.handle_class).mouseover(function(){ 

      if(self.animating) 
       return; 

      self.animate($(this)); 
     }); 
    }, 

    /* 
    * Determines whether handles are above or below their associated section 
    */  
    get_button_position: function(){ 
     return ($(this.container_id).children(":first").hasClass(this.handle_class) ? 'above' : 'below'); 
    }, 

    /* 
    * Animate the accordion from one node to another 
    */ 
    animate: function(handle){ 
     var active_section = (this.button_position == 'below' ? handle.prev() : handle.next());  
     var open_section = handle.siblings().andSelf().filter(".open"); 

     if(active_section.hasClass("open")) 
      return; 

     this.animating = true; 

     // figure out the state of the handles 
     this.do_handle_logic(handle); 

     // Close the open section 
     open_section 
      .syncAnimate(active_section, {"height": "0px"}, {queue:false, duration:this.duration}, '') 
      .removeClass("open"); 

     // Open the new section 
     active_section 
      .syncAnimate(open_section, {"height": this.height}, {queue:false, duration:this.duration}, '') 
      .addClass("open"); 

     var self = this; 
     window.setTimeout(function(){ 
      self.animating = false; 
     }, this.duration); 
    }, 

    /* 
    * Update the current class or "state" of each handle 
    */ 
    do_handle_logic: function(handle){ 
     var all_handles = handle.siblings(".handle").andSelf(); 
     var above_handles = handle.prevAll(this.handle_class); 
     var below_handles = handle.nextAll(this.handle_class); 

     // Remove all obsolete handles 
     all_handles 
      .removeClass("handle_on_above") 
      .removeClass("handle_on_below") 
      .removeClass("handle_off_below") 
      .removeClass("handle_off_above"); 

     // Apply the "on" state to the current handle 
     if(this.button_position == 'below'){ 
      handle 
       .addClass("handle_on_below"); 
     } 
     else{ 
      handle 
       .addClass("handle_on_above"); 
     } 

     // Apply the off above/below state to the rest of the handles 
     above_handles 
      .addClass("handle_off_above"); 

     below_handles 
      .addClass("handle_off_below"); 
    } 
}); 
+0

좀 더 점검 한 후에 이것은 비 -FFF 브라우저에서 여전히 잘 작동하지 않습니다. 여전히 눈에 띄는 지터가 있습니다. 나는 script.pact와 함께 성공적으로 이것을 할 수있었습니다. 병렬 함수. 하지만 난 아직도 jquery로 그것을 할 수있는 방법을 찾고 있어요. –

0

적절한 대기열 및 범위로 jquery에서 병렬 효과를 수행 할 수 없습니다. Scriptaculous는 jQuery가 가지고있는 대기열과 범위를 가지고있어 .queue와 .animate는 기본적으로 쓸모가 없습니다. jQuery가 독창적으로 사용하는 유일한 방법은 돔에서 스타일 속성을 사용하는 것입니다. Scriptaculous는 효과의 가능한 모든 부분을 포함하고 있습니다.

Scriptaculous를 사용해야하고 John Resig는 jQuery.fx를 다시 생각해야합니다. scripty2.com을 살펴보아야합니다.

1

병렬 애니메이션에 대한 정말 멋진 솔루션을 제공하는 Adam Plumb에게 감사드립니다. 나는 그것과 함께 작은 문제가 있었고, 그게 어떻게 든 초기화 애니메이션에서 그들을 저장하기 전에 {}로 규칙을 설정하여 고정 이전 애니메이션에서 역할을 저장했다. 아마 더 나은 방법으로 할 수 있습니다. 애니메이션이 끝났을 때 호출되는 콜백 함수도 추가했습니다.

ParallelAnimations = function(animations, opts){ 
    this.init(animations, opts); 
}; 

$.extend(ParallelAnimations.prototype, { 
    options: { 
     duration: 250, 
     callback: null 
    }, 
    rules: {}, 

    init: function(animations, opts){ 
     // Overwrite the default options 
     $.extend(this.options, opts); 

     // Create a set of rules to follow in our animation 
     this.rules = {}; // Empty the rules. 
     for(var i in animations){ 
      this.rules[i] = { 
       element: animations[i].element, 
       changes: new Array() 
      }; 

      for(var style in animations[i].styles){ 

       // Calculate the start and end point values for the given style change 
       var from = this.parse_style_value(animations[i].element, style, ""); 
       var to = this.parse_style_value(animations[i].element, style, animations[i].styles[style]); 

       this.rules[i].changes.push({ 
        from: from, 
        to: to, 
        style: style 
       }); 
      } 
     } 

     this.start() 
    }, 

    /* 
    * Does some parsing of the given and real style values 
    * Allows for pixel and percentage-based animations 
    */ 
    parse_style_value: function(element, style, given_value){ 
     var real_value = element.css(style); 

     if(given_value.indexOf("px") != -1){ 
      return { 
       amount: given_value.substring(0, (given_value.length - 2)), 
       unit: "px" 
      }; 
     } 

     if(real_value == "auto"){ 
      return { 
       amount: 0, 
       unit: "px" 
      }; 
     } 

     if(given_value.indexOf("%") != -1){ 
      var fraction = given_value.substring(0, given_value.length - 1)/100; 

      return { 
       amount: (real_value.substring(0, real_value.length - 2) * fraction), 
       unit: "px" 
      }; 
     } 

     if(!given_value){ 
      return { 
       amount: real_value.substring(0, real_value.length - 2), 
       unit: "px" 
      }; 
     } 
    }, 

    /* 
    * Start the animation 
    */ 
    start: function(){ 
     var self = this; 
     var start_time = new Date().getTime(); 
     var freq = (1/this.options.duration); 

     var interval = setInterval(function(){ 
      var elapsed_time = new Date().getTime() - start_time; 

      if(elapsed_time < self.options.duration){ 
       var f = elapsed_time * freq; 

       for(var i in self.rules){ 
        for(var j in self.rules[i].changes){ 
         self.step(self.rules[i].element, self.rules[i].changes[j], f); 
        } 
       } 
      } 
      else{ 
       clearInterval(interval); 

       for(var i in self.rules){ 
        for(var j in self.rules[i].changes) 
         self.step(self.rules[i].element, self.rules[i].changes[j], 1); 
       } 
       if(self.options.callback != null) { 
        self.options.callback(); // Do Callback 
       } 
      } 
     }, 10); 
    }, 

    /* 
    * Perform an animation step 
    * Only works with position-based animations 
    */ 
    step: function(element, change, fraction){ 

     var new_value; 
     switch(change.style){ 
      case 'height': 
      case 'width': 
      case 'top': 
      case 'bottom': 
      case 'left': 
      case 'right': 
      case 'marginTop': 
      case 'marginBottom': 
      case 'marginLeft': 
      case 'marginRight': 
       new_value = Math.round(change.from.amount - (fraction * (change.from.amount - change.to.amount))) + change.to.unit; 
       break; 
     } 

     if(new_value) 
      element.css(change.style, new_value); 
    } 
}); 
관련 문제