如何让 jquery 精确并行执行动画? [英] How can I get jquery to execute animations in exact parallel?

查看:20
本文介绍了如何让 jquery 精确并行执行动画?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在 jquery 中创建一个类似于 jquery 的手风琴插件的手风琴小部件,使用我希望句柄出现在它们各自的内容下方而不是上方的区别.我的手风琴通过降低打开内容部分的高度同时增加点击内容部分的高度来工作.我在此处发布了一个示例.我的问题是动画不是在完全相同的时间开始的,并且由于第二个动画开始之前的轻微延迟,有一个明显的跳跃".

Scriptaculous 有一个名为 Effect.Parallel 的函数,它允许您创建一系列动画效果并并行执行它们.不幸的是,我似乎找不到与 jquery 类似的东西.

有没有办法在 jquery 中的单独 div 上运行精确的并行动画?

我对编写这个手风琴小部件的替代方法很感兴趣.因此,如果人们认为还有其他可行的方法,我愿意接受.

解决方案

还有一个答案,希望是最后一个...

不幸的是,John Resig 的 syncAnimate 方法不太适合我想做的手风琴式动画.虽然它在 Firefox 上运行良好,但我无法在 IE 或 Safari 上顺利运行.

话虽如此,我决定咬紧牙关,编写自己的动画引擎来执行简单的并行动画.类代码使用 jquery 函数,但不是 jquery 插件.此外,我仅将其设置为执行大小/位置动画,这正是我所需要的.

ParallelAnimations = function(animations, opts){this.init(animations, opts);};$.extend(ParallelAnimations.prototype, {选项: {持续时间:250},规则:{},初始化:函数(动画,选项){//覆盖默认选项$.extend(this.options, opts);//创建一组规则以在我们的动画中遵循for(var i 在动画中){this.rules[i] = {元素:动画[i].元素,变化:新数组()};for(var style in animations[i].styles){//计算给定样式更改的起点和终点值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({来自:来自,到:到,风格:风格});}}this.start()},/** 对给定的和真实的样式值进行一些解析* 允许基于像素和百分比的动画*/parse_style_value:函数(元素,样式,给定值){var real_value = element.css(style);if(given_value.indexOf("px") != -1){返回 {数量: given_value.substring(0, (given_value.length - 2)),单位:px"};}if(real_value == "auto"){返回 {数量:0,单位:px"};}if(given_value.indexOf("%") != -1){var 分数 = given_value.substring(0, given_value.length - 1)/100;返回 {数量:(real_value.substring(0,real_value.length - 2)*分数),单位:px"};}如果(!给定值){返回 {数量:real_value.substring(0, real_value.length - 2),单位:px"};}},/** 开始动画*/开始:函数(){var self = this;var start_time = new Date().getTime();var freq = (1/this.options.duration);var 间隔 = setInterval(function(){var elapsed_time = new Date().getTime() - start_time;if(elapsed_time < self.options.duration){var f = elapsed_time * 频率;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);}}}别的{清除间隔(间隔);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);},/** 执行动画步骤* 仅适用于基于位置的动画*/步骤:函数(元素,变化,分数){var new_value;开关(改变.风格){案例高度":案例宽度":案例顶部":案例底部":案例左":情况正确":案例'marginTop':案例'marginBottom':案例'marginLeft':案例'marginRight':new_value = Math.round(change.from.amount - (fraction * (change.from.amount - change.to.amount))) + change.to.unit;休息;}如果(新值)element.css(change.style, new_value);}});

那么原来的 Accordion 类只需要在 animate 方法中进行修改就可以使用新的调用了.

Accordion = function(container_id, options){this.init(container_id, options);}$.extend(Accordion.prototype, {容器 ID: '',选项: {},活动标签:0,动画:假,button_position: '下面',持续时间:250,高度:100,handle_class: ".handle",section_class: ".section",初始化:函数(container_id,选项){var self = this;this.container_id = container_id;this.button_position = this.get_button_position();//每个部分的高度,如果可能,使用样式表中指定的高度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;//将第一部分设置为具有高度并打开"//所有其余部分的高度应为 0px$(this.container_id).children(this.section_class).eq(this.active_tab).addClass("打开").css("高度", this.height).siblings(this.section_class).css("高度", "0px");//找出句柄的状态this.do_handle_logic($(this.container_id).children(this.handle_class).eq(this.active_tab));//设置一个事件处理程序来为每个部分设置动画$(this.container_id + " " + this.handle_class).mouseover(function(){如果(自我动画)返回;self.animate($(this));});},/** 确定句柄是在其关联部分的上方还是下方*/获取按钮位置:函数(){return ($(this.container_id).children(":first").hasClass(this.handle_class) ? 'above' : 'below');},/** 将手风琴从一个节点动画到另一个节点*/动画:函数(句柄){var active_section = (this.button_position == 'below' ? handle.prev() : handle.next());var open_section = handle.siblings().andSelf().filter(".open");如果(active_section.hasClass(打开"))返回;this.animating = true;//找出句柄的状态this.do_handle_logic(handle);//关闭打开的部分var arr = new Array();arr.push({元素:open_section,样式:{高度":0px"}});arr.push({元素:active_section,样式:{高度":this.height}});新的 ParallelAnimations(arr, {duration: this.duration});var self = this;window.setTimeout(function(){open_section.removeClass("打开");active_section.addClass("打开");self.animating = false;}, this.duration);},/** 更新每个句柄的当前类或状态"*/do_handle_logic:函数(句柄){var all_handles = handle.siblings(".handle").andSelf();var above_handles = handle.prevAll(this.handle_class);var below_handles = handle.nextAll(this.handle_class);//删除所有过时的句柄all_handles.removeClass("handle_on_above").removeClass("handle_on_below").removeClass("handle_off_below").removeClass("handle_off_above");//将on"状态应用到当前句柄if(this.button_position == '下面'){处理.addClass("handle_on_below");}别的{处理.addClass("handle_on_above");}//将关闭上方/下方状态应用于其余的句柄上面_句柄.addClass("handle_off_above");下面_句柄.addClass("handle_off_below");}});

HTML 的调用方式仍然相同:

<头><title>平行手风琴动画</title><script type="text/javascript" src="jquery.js"></script><script type="text/javascript" src="ui.js"></script><script type="text/javascript">$(document).ready(function(){新手风琴(#accordion");});<style type="text/css">#手风琴{位置:相对;}#手风琴.句柄{宽度:260px;高度:30px;背景颜色:橙色;}#手风琴.section{宽度:260px;高度:445px;背景颜色:#a9a9a9;溢出:隐藏;位置:相对;}</风格><身体><div id="手风琴"><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>

我将来可能会添加一些内容:- 排队动画- 其他类型样式(颜色等)的动画

I'm trying to create an accordion widget in jquery similar to jquery's accordion plugin, with the difference that I want the handles to appear below their respective content instead of above. My accordion works by decreasing the height of the open content section while at the same time increasing the height of the clicked content section. I've posted an example here. My problem is that the animations aren't started at exactly the same time, and there is a noticeable "jump" due to the slight delay before the second animation is started.

Scriptaculous has a function called Effect.Parallel that allows you to create an array of animation effects and execute them in parallel. Unfortunately, I can't seem to find something similar with jquery.

Is there a way I can run precise parallel animations on separate divs in jquery?

Edit: I'm as much interested in alternative methods of coding this accordion widget. So if there is any other method people think would work I'm open to that.

解决方案

One more answer, hopefully my last one...

Unfortunately, John Resig's syncAnimate method is not quite up to snuff for the accordion-type animation I want to do. While it works great on Firefox, I couldn't get it working smoothly on IE or Safari.

With that said, I decided to bite the bullet and write my own animation engine that does simple parallel animations. The class-code uses jquery functions but is not a jquery plugin. Also, I've only set it up to do size/position animations, which is all I need.

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);
    }
});

Then the original Accordion class only needs to be modified in the animate method to make use of the new call.

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");
    }
});

The HTML is still called the same way:

<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>

There are a few things I may add in the future: - Queued Animations - Animations for other types of styles (colors,etc)

这篇关于如何让 jquery 精确并行执行动画?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆