3.jQuery實現(xiàn)拖拽

1.index.html填充靜態(tài)數(shù)據(jù)

在index.html頁面id="user-choosn-tool"的元素下拷貝5個圖標(biāo)

<div class="ico_03 color ico_box" id="user-choosn-tool" style="position: relative;">
    <dl class="drag-element cross-element" data-index="0" style="left: 0px; top: 20px;">
        <a href="javascript:void(0);">
            <dd>
                <img src="/images/ico_1.png">
            </dd>
            <dt name="Portal_Reimburse">我的報銷</dt>
        </a>
    </dl>
    <dl class="drag-element cross-element" data-index="1" style="left: 100px; top: 20px;">
        <a href="javascript:void(0);">
            <dd>
                <img src="/images/ico_2.png">
            </dd>
            <dt name="Portal_Reimburse">我的考勤</dt>
        </a>
    </dl>
    <dl class="drag-element cross-element" data-index="2" style="left: 200px; top: 20px;">
        <a href="javascript:void(0);">
            <dd>
                <img src="/images/ico_3.png">
            </dd>
            <dt name="Portal_Reimburse">通訊錄</dt>
        </a>
    </dl>
    <dl class="drag-element cross-element" data-index="3" style="left: 300px; top: 20px;">
        <a href="javascript:void(0);">
            <dd>
                <img src="/images/ico_4.png">
            </dd>
            <dt name="Portal_Reimburse">請假管理</dt>
        </a>
    </dl>
    <dl class="" id="submit-btn" style="left: 400px; top: 20px;">
        <a href="javascript: void(0);">
            <dd class="tj_ico">
                <img src="/images/ico_bc_2.png">
            </dd>
            <dt>保存</dt>
        </a>
    </dl>
    <div class="clear"></div>
</div>

啟動項目,在瀏覽器中打開http://localhost:3000户辞,頁面輸出如下圖

image.png

2.拖拽

2.1 添加drag.js

在javascripts文件夾下新建drag.js文件翔横,然后在index.html頁面引入下面的三個js文件

<script type="text/javascript" src="/javascripts/jquery-1.10.2.min.js"></script>
<script type="text/javascript" src="/javascripts/jquery.easing.min.js"></script>
<script type="text/javascript" src="/javascripts/drag.js"></script>

2.2 添加Drag構(gòu)造函數(shù)

drag.js的代碼如下,這里使用閉包啊掏,防止污染外部變量

(function ($) {
    function Drag(el, options){
        this.$el = el;
        this.options = options;

        this.init();
    }

    Drag.prototype = {
        init: function() {
            console.log('init')
        }
    }

    var DEFAULTS = {
        duration: 500,  //運動持續(xù)時間
        easing: 'easeInOutBack',    // 動畫效果
        submitBtn: '#submit-btn',  //提交按鈕
        leftDistance: 100, // 每個元素間的間距
        dragEleClass: '',   // 可拖拽的一組元素
        dragLimitId: '',  // 拖拽元素的父元素蠢络,限制拖拽元素的拖拽區(qū)域
        crossEleClass: '.cross-element',   // 跟哪組元素做碰撞
        crossChangeEle: 'dd',   //碰撞之后要改變的元素
        crossChangeStyle: 'borderColor',    //碰撞元素要改變的樣式
        crossChangeValue: '#f00',   //碰撞元素要改變成什么樣式值
        dragAfter: function () { }    //拖拽之后執(zhí)行的方法
    };

    // 使用jQuery添加一個實例方法,讓外界元素調(diào)用
    $.fn.drag = function(option){
        var options = $.extend(true, {}, DEFAULTS, typeof option === 'object' && option),
            args = Array.prototype.slice.call(arguments);
        args.shift();

        return this.each(function(){
            var $el = $(this),
                instance = $el.data('drag');
            
            if(!instance){
                $el.data('drag', (instance = new Drag($el, options)));
            }

            if(typeof option === 'string'){
                instance[option].apply(instance, args);
            }
        });
    }
})(jQuery);

2.3 添加index.js

在javascripts文件夾下新建一個index.js文件迟蜜,并且在index.html引入該文件刹孔,index.js文件代碼如下

(function ($) {
    $('#user-choosn-tool').find('.drag-element').drag({
        'dragLimitId': '#user-choosn-tool',
        'dragEleClass': '.drag-element'
    });
})(jQuery);

到這里刷新瀏覽器,可以看到控制臺上打印了4次 "init"


image.png

2.4 定位元素

image.png

如上圖娜睛,在index.html頁面里面髓霞,每個圖標(biāo)的left值都是寫死的,在實際應(yīng)用中這樣肯定是不合理的畦戒,我們需要在程序里計算出每個圖標(biāo)的位置方库,此時drag.js的部分代碼如下

Drag.prototype = {
    init: function () {
        var self = this,
            opt = self.options;

        self.leftDistance = opt.leftDistance;
        self.$limit = $(opt.dragLimitId);
        self.$dragEles = self.$limit.find(opt.dragEleClass);
        self.$btn = $(opt.submitBtn);

        self.setPosition();
        self.initLocation();
        self.initBtnLocation();

        return self;
    },
    // 初始化元素位置
    initLocation: function () {
        var self = this,
            leftDistance = self.leftDistance,
            $el = self.$el;
            $limit = self.$limit;

        var left = self.getIndex($el) * leftDistance,
            top = 20;

        $el.css({
            'left': left,
            'top': top
        });

        return self;
    },
    // 初始化保存按鈕的位置
    initBtnLocation: function () {
        var self = this,
            $lastDragPos = self.$dragEles.last().position(),
            btnLeft = Math.ceil($lastDragPos.left + self.leftDistance),
            btnTop = $lastDragPos.top;
        
        self.$btn.css({
            'position': 'absolute',
            'left': btnLeft,
            'top': btnTop
        });

        return self;
    },
    // 給父元素設(shè)置定位
    setPosition: function () {
        var $limit = this.$limit;

        if( $limit.length > 0 && $limit.css('position') === 'static' ){
            $limit.css('position', 'relative');
        }

        return this;
    },
    getIndex: function ($el) {
        return $el.attr('data-index') || $el.data('index') || $el.index();
    }
}

到這里,就可以把index.html頁面的那些定位樣式去掉了障斋,刷新瀏覽器可以看到頁面展示效果一致

2.5 綁定拖拽事件

給可拖拽的元素綁定事件纵潦,drag.js的部分代碼如下

init: function () {
    var self = this,
        opt = self.options;
    
    self.leftDistance = opt.leftDistance;
    self.$limit = $(opt.dragLimitId);
    self.$dragEles = self.$limit.find(opt.dragEleClass);
    self.$crossEles = opt.crossEleClass ? $(opt.crossEleClass) : self.$dragEles;
    self.$btn = $(opt.submitBtn);
    self.originVal = self.$dragEles.find(opt.crossChangeEle).css(opt.crossChangeStyle);

    self.setPosition();
    self.initLocation();
    self.initBtnLocation();
    self.bindEvent();

    return self;
},
bindEvent: function () {
    var self = this,
        opt = self.options,
        $el = self.$el,
        $limit = self.$limit;
    
    $el.mousedown(function(e){
        e.preventDefault();

        var isAnimated = false;
        self.$dragEles.each(function(){
            if($(this).is(':animated')){
                isAnimated = true;
                return false;
            }
        });

        if(isAnimated){
            return;
        }
        
        var pos = $el.position(),
            disX = e.pageX - pos.left,
            disY = e.pageY - pos.top,
            $nearest = '';

        // 拖拽之前先保留一些信息
        $el.data({
            'originLeft': pos.left,
            'originTop': pos.top,
            'zIndex': $el.css('zIndex') || 1
        });

        $(document).on('mousemove.drag', function(e){
            $el.css({
                'left': e.pageX - disX,
                'top': e.pageY - disY,
                'zIndex': 999
            });

            if ($limit.length > 0) {
                self.checkDragPos();
            }

            $nearest = self.nearest();
            
            if ($nearest) {
                self.$crossEles.find(opt.crossChangeEle).css(opt.crossChangeStyle, self.originVal);
                $nearest.find(opt.crossChangeEle).css(opt.crossChangeStyle, opt.crossChangeValue);
            } else {
                self.$crossEles.find(opt.crossChangeEle).css(opt.crossChangeStyle, self.originVal);
            }
        }).on('mouseup.drag', function(){
            $(this).off('.drag');

            typeof self.options.dragAfter === 'function' && self.options.dragAfter.call(self);

            // 拖拽結(jié)束徐鹤,如果沒有最近的元素就把位置還原
            if (!$nearest) {
                $el.animate({
                    'left': $el.data('originLeft'),
                    'top': $el.data('originTop')
                }, opt.duration, opt.easing, function(){
                    $el.css('zIndex', $el.data('zIndex'));
                });
            } else {
                self.moveDragPos($nearest);
            }
        });
    });

    return self;
}

2.6 檢查拖拽元素位置

在拖拽的過程中,需要時刻檢測拖拽元素的位置邀层,不能讓拖拽元素拖到limit元素的外面去

checkDragPos: function () {
    var self = this,
        $el = self.$el,
        $limit = self.$limit,
        pos = $el.position(),
        maxTop = $limit.outerHeight() - $el.outerHeight(true),
        maxLeft = $limit.outerWidth() - $el.outerWidth(true);

    if(pos.top < 0){
        $el.css('top', 0);
    } else if(pos.top > maxTop){
        $el.css('top', maxTop);
    }

    if(pos.left < 0){
        $el.css('left', 0);
    } else if(pos.left > maxLeft){
        $el.css('left', maxLeft);
    }

    return self;
}

2.7 獲取離拖拽元素最近的元素

拖拽過程中返敬,還需要時刻檢查離拖拽元素最近的元素

nearest: function () {
    var self = this,
        $el = self.$el,
        $eles = self.$crossEles;
        value = 9999,
        index = -1;

    for(var i = 0; i < $eles.length; i++){
        var $obj = $eles.eq(i);

        if($el.attr('data-index') == $obj.attr('data-index')){
            continue;
        }

        if(self.isCross($el, $obj)){
            var d = self.distance($el, $obj);
            if(d < value){
                value = d;
                index = i;
            }
        }
    }

    if(index !== -1){
        return $eles.eq(index);
    } else{
        return false;
    }
}

2.8 碰撞檢測

拖拽過程中,判斷nearest元素是否和拖拽元素有重疊寥院,如果重疊需要給重疊的元素設(shè)置一些樣式劲赠,提升用戶體驗

isCross: function ($el1, $el2) {
    var pos1 = $el1.position(),
        pos2 = $el2.position(),
        t1 = pos1.top,
        b1 = pos1.top + $el1.innerHeight(),
        l1 = pos1.left,
        r1 = pos1.left + $el1.innerWidth(),
        t2 = pos2.top,
        b2 = pos2.top + $el2.innerHeight(),
        l2 = pos2.left,
        r2 = pos2.left + $el2.innerWidth();
    
    if(t1 > b2 || b1 < t2 || r1 < l2 || l1 > r2){
        return false;
    } else{
        return true;
    }
}

2.9 移動元素位置

拖拽結(jié)束時(mouseup事件),如果有重疊的元素秸谢,就需要移動元素的位置

moveDragPos: function ($nearest) {
    var self = this,
        opt = self.options,
        index = $nearest.attr('data-index'),
        length = self.$el.attr('data-index');
    
    self.$crossEles.each(function(){
        var $this = $(this);
        if($this.get(0) != self.$el.get(0)){
            $this.data({
                'originTop': $this.position().top,
                'originLeft': $this.position().left,
                'originIndex': $this.attr('data-index')
            });
        }
    });
    
    if(length > index){ // 向前拖拽
        for(var i = length - 1; i >= index; i--){
            (function(i){
                var $ele = self.$crossEles.eq(0).parent().find('[data-index=' + i + ']'),
                    $nextEle = self.$crossEles.eq(0).parent().find('[data-index=' + (i + 1) + ']'),
                    top = $nextEle.data('originTop'),
                    left = $nextEle.data('originLeft'),
                    _index = Number($ele.attr('data-index'));
                $ele.delay( (i - index) * 80 ).animate({
                    'top': top,
                    'left': left //'+=100px'
                }, 200, 'linear', function(){
                    $(this).attr('data-index', _index + 1).find(opt.crossChangeEle).css(opt.crossChangeStyle, self.originVal);
                });
                if(i == index){
                    self.timer = setTimeout(function(){
                        self.$el.animate({
                            'top': $nearest.data('originTop'),
                            'left': $nearest.data('originLeft')
                        }, 100, 'linear', function(){
                            $(this).attr('data-index', $nearest.data('originIndex')).css('zIndex', $(this).data('zIndex'));
                        });
                    }, (length - index) * 110);
                }
            })(i);
        }
    } else if(length < index){  // 向后拖拽
        for(var i = index; i > length; i--){
            (function(i){
                var $ele = self.$crossEles.eq(0).parent().find('[data-index=' + i + ']'),
                    $nextEle = self.$crossEles.eq(0).parent().find('[data-index=' + (i - 1) + ']'),
                    top = $nextEle.data('originTop'),
                    left = $nextEle.data('originLeft'),
                    _index = Number($ele.attr('data-index'));
                $ele.delay( (index - i) * 80 ).animate({
                    'top': top,
                    'left': left //'-=100px'
                }, 200, 'linear', function(){
                    $(this).attr('data-index', _index - 1).find(opt.crossChangeEle).css(opt.crossChangeStyle, self.originVal);
                });
                if(i == index){
                    self.timer = setTimeout(function(){
                        self.$el.animate({
                            'top': $nearest.data('originTop'),
                            'left': $nearest.data('originLeft')
                        }, 100, 'linear', function(){
                            $(this).attr('data-index', $nearest.data('originIndex')).css('zIndex', $(this).data('zIndex'));
                        });
                    }, (index - length) * 110);
                }
            })(i);
        }
    }
}

到這里拖拽的功能基本就完成了凛澎,下一篇就是從接口獲取數(shù)據(jù)了

git倉庫地址

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市钮追,隨后出現(xiàn)的幾起案子预厌,更是在濱河造成了極大的恐慌,老刑警劉巖元媚,帶你破解...
    沈念sama閱讀 219,589評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件轧叽,死亡現(xiàn)場離奇詭異,居然都是意外死亡刊棕,警方通過查閱死者的電腦和手機炭晒,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,615評論 3 396
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來甥角,“玉大人网严,你說我怎么就攤上這事∴臀蓿” “怎么了震束?”我有些...
    開封第一講書人閱讀 165,933評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長当犯。 經(jīng)常有香客問我垢村,道長,這世上最難降的妖魔是什么嚎卫? 我笑而不...
    開封第一講書人閱讀 58,976評論 1 295
  • 正文 為了忘掉前任嘉栓,我火速辦了婚禮,結(jié)果婚禮上拓诸,老公的妹妹穿的比我還像新娘侵佃。我一直安慰自己,他們只是感情好奠支,可當(dāng)我...
    茶點故事閱讀 67,999評論 6 393
  • 文/花漫 我一把揭開白布馋辈。 她就那樣靜靜地躺著,像睡著了一般胚宦。 火紅的嫁衣襯著肌膚如雪首有。 梳的紋絲不亂的頭發(fā)上燕垃,一...
    開封第一講書人閱讀 51,775評論 1 307
  • 那天枢劝,我揣著相機與錄音井联,去河邊找鬼。 笑死您旁,一個胖子當(dāng)著我的面吹牛烙常,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鹤盒,決...
    沈念sama閱讀 40,474評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼蚕脏,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了侦锯?” 一聲冷哼從身側(cè)響起驼鞭,我...
    開封第一講書人閱讀 39,359評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎尺碰,沒想到半個月后挣棕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,854評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡亲桥,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,007評論 3 338
  • 正文 我和宋清朗相戀三年洛心,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片题篷。...
    茶點故事閱讀 40,146評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡词身,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出番枚,到底是詐尸還是另有隱情法严,我是刑警寧澤,帶...
    沈念sama閱讀 35,826評論 5 346
  • 正文 年R本政府宣布葫笼,位于F島的核電站深啤,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏渔欢。R本人自食惡果不足惜墓塌,卻給世界環(huán)境...
    茶點故事閱讀 41,484評論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望奥额。 院中可真熱鬧苫幢,春花似錦、人聲如沸垫挨。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,029評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽九榔。三九已至哀峻,卻和暖如春涡相,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背剩蟀。 一陣腳步聲響...
    開封第一講書人閱讀 33,153評論 1 272
  • 我被黑心中介騙來泰國打工催蝗, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人育特。 一個月前我還...
    沈念sama閱讀 48,420評論 3 373
  • 正文 我出身青樓丙号,卻偏偏與公主長得像,于是被迫代替她去往敵國和親缰冤。 傳聞我的和親對象是個殘疾皇子犬缨,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,107評論 2 356

推薦閱讀更多精彩內(nèi)容