zhangxinxu / quiz

小测答题收集区
535 stars 43 forks source link

DOM基础测试39 #49

Open zhangxinxu opened 4 years ago

zhangxinxu commented 4 years ago

本期小测温习下dialog元素,以及常见的拖拽排序实现。

素材图大家自己随便找找,建议用大图,实在找不到可以使用 www.qidian.com 上的书封图。

积分:3+3

大家提交回答的时候,注意缩进距离,起始位置从左边缘开始;另外,github自带代码高亮,所以请使用下面示意的格式(缩进和代码高亮1积分)。

```js
// 你的JS代码写在这里
 ```

**心怀瑞雪,自己作答,不要参考别人回答**

其他 本周六11月2日上午10:00答疑,直播地址:https://live.bilibili.com/21193211

每位答题者会有至少2积分参与分,本次小测满分10积分。

首位答题者将会获得100%被翻牌的技能。

XboxYan commented 4 years ago

demo

<div class="img-list" id="img-list">
    <img class="img-thumb" src="https://images.pexels.com/photos/2387874/pexels-photo-2387874.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/2387874/pexels-photo-2387874.jpeg?auto=compress&cs=tinysrgb&w=1560">
    <img class="img-thumb" src="https://images.pexels.com/photos/2524767/pexels-photo-2524767.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/2524767/pexels-photo-2524767.jpeg?auto=compress&cs=tinysrgb&w=940">
    <img class="img-thumb" src="https://images.pexels.com/photos/2669181/pexels-photo-2669181.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/2669181/pexels-photo-2669181.jpeg?auto=compress&cs=tinysrgb&w=940">
    <img class="img-thumb" src="https://images.pexels.com/photos/2865901/pexels-photo-2865901.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/2865901/pexels-photo-2865901.jpeg?auto=compress&cs=tinysrgb&w=1560">
    <img class="img-thumb" src="https://images.pexels.com/photos/2159505/pexels-photo-2159505.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/2159505/pexels-photo-2159505.jpeg?auto=compress&cs=tinysrgb&w=1280">
    <img class="img-thumb" src="https://images.pexels.com/photos/2847766/pexels-photo-2847766.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/2847766/pexels-photo-2847766.jpeg?auto=compress&cs=tinysrgb&w=940">
    <img class="img-thumb" src="https://images.pexels.com/photos/2089799/pexels-photo-2089799.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/2089799/pexels-photo-2089799.jpeg?auto=compress&cs=tinysrgb&w=1560">
    <img class="img-thumb" src="https://images.pexels.com/photos/1580173/pexels-photo-1580173.jpeg?auto=compress&cs=tinysrgb&w=200" data-src="https://images.pexels.com/photos/1580173/pexels-photo-1580173.jpeg?auto=compress&cs=tinysrgb&w=940">
</div>
<dialog id="dialog"><img class="img-preview" id="img-preview" draggable="false" src=""></dialog>
.img-list {
    width: 420px;
    height: 420px;
    display: grid;
    grid-gap: 10px;
    grid-template-columns: repeat(3, 1fr);
    grid-template-rows: repeat(3, 1fr);
    border: 10px solid #eee;
    padding: 10px;
}

.img-thumb {
    width: 100%;
    height: 100%;
    background: #eee;
    object-fit: cover;
    border: 0;
}

.img-thumb[dragging] {
    box-shadow: 5px 5px 15px rgba(0, 0, 0, .2)
}

.img-preview {
    max-width: 100%;
    max-height: 100%;
    transition: .3s;
    visibility: hidden;
    transform: scale(.5);
}

dialog {
    display: flex;
    box-sizing: border-box;
    position: fixed;
    justify-content: center;
    align-items: center;
    padding: 20px;
    border: 0;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, .5);
    font-size: 20px;
    color: #fff;
    transition: .3s;
}

dialog:not([open]) {
    display: flex;
    visibility: hidden;
    opacity: 0;
}

dialog[open] {
    visibility: visible;
    opacity: 1;
}

dialog[open]:not([loading]) .img-preview {
    visibility: visible;
    transform: scale(1);
}

dialog[open][loading]::before {
    position: absolute;
    content: "loading...";
}
var imgList = document.getElementById('img-list');
var dialog = document.getElementById('dialog');
var imgPreview = document.getElementById('img-preview');
imgList.addEventListener('click', function(ev) {
    if (ev.target.tagName === 'IMG') {
        dialog.setAttribute('loading','');
        dialog.show();
        var img = new Image();
        img.src = ev.target.dataset.src;
        img.onload = function() {
            dialog.removeAttribute('loading');
            imgPreview.src = img.src;
        }
    }
})
dialog.addEventListener('click', function(ev) {
    if (ev.target.tagName === 'DIALOG') {
        this.close();
    }
})

var currentDragItem = null;

imgList.querySelectorAll(':scope .img-thumb').forEach(function(el) {
    el.addEventListener('dragstart', function(ev) {
        currentDragItem = this;
    })
    el.addEventListener('dragend', function(ev) {
        currentDragItem = null;
    })
})
imgList.ondragover = function(ev) {
    ev.preventDefault();
    if (!currentDragItem) { return }
    if (ev.target.tagName === 'IMG') {
        if (currentDragItem.compareDocumentPosition(ev.target) == 2) {
            ev.target.before(currentDragItem);
        } else {
            ev.target.after(currentDragItem);
        }
    }
}
//zxx: 还需要ondrop的默认行为阻止
//xboxyan: 看来和chrome的版本有很大关系,showModal在某个版本下不支持动画,最新的可以,ondrop的默认行为好像也和版本有关系。。另外,只有showModal才能默认用Esc关闭,show不行。

第二题使用原生拖拽实现,为了更好的效果,使用了最近刚刚写的一个美化polyfill

les-lee commented 4 years ago

又把API熟悉了一遍... demo

//zxx: 点击弹框没出现,拖拽交互体验可以提高

// leslee: 尴尬... 类选择器忘记加点 导致点击事件没能添加成功... 因为想赶个第一没检查就发布...下次会留意, 质在量的前面.
<div class="domtest39">
    <img draggable=" true" ondragenter="dragenter(event)" ondragover="return false" ondragend="dragend(event)"
      src="http://i.ce.cn/newwap/fa/gd/201801/05/W020180105553424002522.jpg" alt="张国荣">
    <img draggable="true" ondragenter="dragenter(event)" ondragover="return false" ondragend="dragend(event)"
      src="http://i.ce.cn/newwap/fa/gd/201801/05/W020180105553424002522.jpg" alt="张国荣">
    <img draggable="true" ondragenter="dragenter(event)" ondragover="return false" ondragend="dragend(event)"
      src="http://i.ce.cn/newwap/fa/gd/201801/05/W020180105553424002522.jpg" alt="张国荣">
    <img draggable="true" ondragend="dragend(event)" ondragover="return false" ondragenter="dragenter(event)"
      src="http://n.sinaimg.cn/sinacn/w542h725/20180124/5208-fyqwiqk2138344.jpg" alt="张国荣">
  </div>
body,
  img {
    padding: 0;
    margin: 0;
  }

  .domtest39 {
    width: 260px;
    font-size: 0;
    /* margin-left: -10px; */
  }

  .domtest39 img {
    width: 80px;
    height: 80px;
    margin-left: 10px;
    margin-bottom: 10px;
  }

  .domtest39 img:nth-child(3n-2) {
    margin-left: 0;
  }
document.querySelectorAll('.domtest39 img').forEach((item) => {

      item.addEventListener('click', function (e) {
        let dialog = document.createElement('dialog')
        document.body.append(dialog)
        let img = document.createElement('img')
        img.width = window.innerWidth * 0.8
        img.src = e.target.src
        dialog.append(img)
        dialog.showModal('gg')
      })
    })

    document.addEventListener('click', e => e.target.close ? e.target.close() : '')

    // 2  
    let enterTarget
    function dragend(event) {
      let parentNode = event.target.parentElement
      let enterTempNode = enterTarget.cloneNode(true)
      let targetTempNode = event.target.cloneNode(true)
      parentNode.insertBefore(enterTempNode, event.target)
      parentNode.removeChild(event.target)
      parentNode.insertBefore(targetTempNode, enterTarget)
      parentNode.removeChild(enterTarget)
    }
    function dragenter(event) {
      enterTarget = event.target
    }
fzpijl commented 4 years ago
//zxx: 没demo,永远移不到第3张素材的后面
<div>
    <img src="https://dpic.tiankong.com/pc/xt/QJ6262880499.jpg?x-oss-process=style/794ws" />
    <img src="https://dpic.tiankong.com/pu/be/QJ6266116275.jpg?x-oss-process=style/794ws" />
    <img src="https://dpic.tiankong.com/pr/6x/QJ6292442803.jpg?x-oss-process=style/794ws" />
    <img src="https://dpic.tiankong.com/p9/uj/QJ6267246227.jpg?x-oss-process=style/794ws" />
</div>
<dialog></dialog>
div {
    width: 400px;
    overflow: hidden;
}

div img {
    float: left;
    width: 120px;
    margin-right: 20px;
    margin-bottom: 10px;
    height: 80px;
}

img:nth-child(3n) {
    margin-right: 0;
}
// 1.
var div = document.querySelector('div')
var dialog = document.querySelector('dialog')
div.addEventListener('click', function (e) {
    var target = e.target;
    if (target.tagName === 'IMG') {
        var newImg = target.cloneNode(true)
        dialog.append(newImg)
        dialog.showModal()
    }
})
dialog.addEventListener('click', function () {
    dialog.removeChild(dialog.firstElementChild)
    dialog.close()
})
// 2.
div.addEventListener('dragover', function (e) {
    e.preventDefault()
})
div.addEventListener('drop', function (e) {
    e.preventDefault()
})
var imgs = document.querySelectorAll('img'),
    dragElement = null;
for (var i = 0; i < imgs.length; i++) {
    imgs[i].addEventListener('dragstart', function (ev) {
        dragElement = this;
    });

    imgs[i].addEventListener('dragenter', function (ev) {
        if (dragElement != this) {
            if(this.parentNode.lastElementChild === this) {
                this.parentNode.append(dragElement)
            }else {
                this.parentNode.insertBefore(dragElement, this);
            }
        }
    })
};
rayj1993 commented 4 years ago

demo

    let obj = null
    let box = document.getElementById('box')
    document.addEventListener('mouseup', function (e) {
        const target = e.target
        if (target.nodeName === 'IMG' && box.contains(target)) {
            if (obj) {
                obj.remove()
            }
            let newDialog = document.createElement('dialog')
            // newDialog.setAttribute('open', '开启')
            let imgBox = document.createElement('img')
            imgBox.setAttribute('src', target.src.replace(/90$/, '180'))
            newDialog.appendChild(imgBox)
            newDialog.setAttribute('id', 'favDialog')
            obj = newDialog
            document.body.appendChild(newDialog)
            var dialog = document.getElementById('favDialog')
            dialog.showModal()
        } else {
            if (obj) {
                obj.remove()
            }
        }
    })

   // 存储变量
   var dragged;

   document.addEventListener("dragstart", function (event) {
       dragged = event.target;
   }, false);

   /* 放下目标节点时触发事件 */
   document.addEventListener("dragover", function (event) {
       // 阻止默认动作
       event.preventDefault();
   }, false);

   document.addEventListener("dragenter", function (event) {
       if (event.target.nodeName === 'IMG') {
           var enterIndex = [].indexOf.call(box.childNodes, event.target);
           var index = [].indexOf.call(box.childNodes, dragged);
           if (index > enterIndex) {
               dragged.parentNode.insertBefore(dragged, event.target);
           } else {
               dragged.parentNode.insertBefore(dragged, event.target.nextSibling);
           }
       }

   }, false);
juzhiqiang commented 4 years ago

demo html

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div class="img-group">
    <img data-big_src="//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/7eaa8bd8fc72d4f536c17be887df8238.png" src="//bossaudioandcomic-1252317822.image.myqcloud.com/activity/document/7eaa8bd8fc72d4f536c17be887df8238.png"/>
    <img data-big_src="https://img2.qidian.com/upload/gamesy/2019/10/22/20191022182743obsvqpbdqh.png" src="https://img2.qidian.com/upload/gamesy/2019/10/22/20191022182743obsvqpbdqh.png"/>
    <img data-big_src="//qidian.qpic.cn/qidian_common/349573/bd326aea6e0a7ca656ed6a4011745b90/0" src="//qidian.qpic.cn/qidian_common/349573/bd326aea6e0a7ca656ed6a4011745b90/0"/>
    <img data-big_src="https://img2.qidian.com/upload/gamesy/2019/09/29/20190929144145e9ir20e3dy.jpg" src="https://img2.qidian.com/upload/gamesy/2019/09/29/20190929144145e9ir20e3dy.jpg"/>
  </div>

  <dialog id="js-dalog">
    <img src=""/>
  </dialog>

</body>
</html>

css

#js-dalog img{
  width: 360px;
}

js

let imgs = document.querySelectorAll('.img-group img');
let dalog = document.querySelector('#js-dalog');
let bigImg = document.querySelector('#js-dalog img');

imgs.forEach( item =>{
  item.onclick = function(e){
    let bigSrc = this.getAttribute('data-big_src');
    bigImg.setAttribute('src',bigSrc);
    dalog.showModal()
  };

})

//点击任意位置隐藏
dalog.onclick = function(e){
   dalog.close();
}

//2.
let nowImg;
document.querySelector('.img-group').ondragstart= function(e){
    if(e.target.tagName === 'IMG'){
      nowImg = e.target;
    }
}

document.querySelector('.img-group').addEventListener("dragover",function(e){
  if(e.target.tagName === 'IMG'){
     let drag = e.target;
      drag.parentNode.insertBefore(nowImg,drag.nextElementSibling);
  }
},false);
liyongleihf2006 commented 4 years ago

jsbin

//zxx: 移不到最后,交互上可以调整下
.img-thumb{
    height:100px;
    margin:0 10px 10px 0;
}
<div id="img-container">

    <img class="img-thumb" draggable="true" src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=533762546,672570916&fm=26&gp=0.jpg" />

    <img class="img-thumb" draggable="true" src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=537501145,2320471355&fm=26&gp=0.jpg" />

    <img class="img-thumb" draggable="true" src="https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=3082876337,1693663170&fm=26&gp=0.jpg" />

    <img class="img-thumb" draggable="true" src="https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=423551398,1303118808&fm=26&gp=0.jpg" />

    <img class="img-thumb" draggable="true" src="https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=2595508360,28762262&fm=26&gp=0.jpg" />

</div>
<dialog id="dialog"><img id="img-preview"/></dialog>
var container = document.querySelector("#img-container");
var dialog =document.querySelector("#dialog");
container.addEventListener("click",function(e){
    var target = e.target;
    if(target.classList.contains('img-thumb')){
        document.querySelector("#img-preview").src = target.src;
        dialog.showModal();
    }
})
dialog.addEventListener("click",function(){
    this.close();
})

var dragImg;
container.addEventListener("dragstart", function( event ) {
    dragImg = event.target;
}, false);

document.addEventListener("dragover", function( event ) {
    event.preventDefault();
}, false);

container.addEventListener("drop", function( event ) {
    event.preventDefault();
    var target = event.target;
    if(target.classList.contains("img-thumb")){
        target.parentNode.insertBefore(dragImg,target);
    }
}, false);
Despair-lj commented 4 years ago

demo

<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <title>JS Bin</title>
</head>
<body>
  <div class="container">
    <img src="https://avatars2.githubusercontent.com/u/8694868" alt="avatars" crossorigin="anonymous" />
    <img src="https://avatars0.githubusercontent.com/u/8696309" alt="avatars" crossorigin="anonymous" />
    <img src="https://avatars3.githubusercontent.com/u/8697294" alt="avatars" crossorigin="anonymous" />
    <img src="https://avatars1.githubusercontent.com/u/8669103" alt="avatars" crossorigin="anonymous" />
    <img src="https://avatars1.githubusercontent.com/u/40731029" alt="avatars" crossorigin="anonymous" />
    </div>
  <dialog class="dialog-container">
    <!-- zxx: 建议remove src属性 -->
    <img src="" alt="dialog show img" />
  </dialog>
  <canvas></canvas>
</body>
</html>
// 1
var container = document.querySelector('.container');
var dialog = document.querySelector('.dialog-container');
var showImg = dialog.querySelector('img');
var canvas = document.querySelector('canvas');
var ctx = canvas.getContext('2d');

container.addEventListener('click', function(event) {
  target = event.target;
  if (target.tagName.toLowerCase() === 'img') {
    // 显示图片,防止重复申请图片资源
    var bodySize = document.body.getBoundingClientRect();
    var width =
        target.naturalWidth > bodySize.width - 30
    ? bodySize.width - 30
    : target.naturalWidth;
    var height = (width * target.naturalHeight) / target.naturalWidth;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(target, 0, 0, width, height);
    // toDataURL 方法涉及到跨域,所以要在 img 添加属性 crossorigin="anonymous"
    // var img = canvas.toDataURL();
    // showImg.src = img;
    // 使用异步 api toBlob
    canvas.toBlob(function(blob) {
      var url = URL.createObjectURL(blob);
      showImg.src = url;
      // dialog.showModal(); 
      // 兼容 firefox 和 safari 
      dialog.setAttribute('open', '');
    });

  }
});

dialog.addEventListener('click', function(event) {
  if (event.target.tagName.toLowerCase() === 'dialog') {
    // 兼容 firefox 和 safari 
    event.target.removeAttribute('open')
  }
});

document.addEventListener('keydown', function(event) {
  console.log(event.key);
  if (event.key === 'Escape' && dialog.hasAttribute('open')) {
    // dialog.close();
    // 兼容 firefox 和 safari 
    dialog.removeAttribute('open');
  }
})

// 2
var dragElement, dragIndex, enterElement, enterIndex;
document.addEventListener(
  'dragstart',
  function(event) {
    dragElement = event.target;
    dragElement.style.opacity = 0.5;
    document.querySelectorAll('img').forEach(function(img, index) {
      if (img === dragElement) {
        dragIndex = index;
      }
    });
    // 图片过大拖拽会显示不出图片,选用 canvas 生成图片拖拽效果
    var ctx = canvas.getContext('2d');
    var width = dragElement.width;
    var height = dragElement.height;
    canvas.width = width;
    canvas.height = height;
    ctx.drawImage(dragElement, 0, 0, width, height);
    var dt = event.dataTransfer;
    dt.setDragImage(canvas, 25, 25);
  },
  false
);
document.addEventListener(
  'dragend',
  function(event) {
    event.target.style.opacity = '';
  },
  false
);

document.addEventListener(
  'dragover',
  function(event) {
    // 删除图片返回原始位置的效果
    event.preventDefault();
  },
  false
);

document.addEventListener(
  'dragenter',
  function(event) {
    enterElement = event.target;
    if (
      event.target.tagName.toLowerCase() !== 'img' ||
      enterElement === dragElement
    ) {
      return;
    }
    document.querySelectorAll('img').forEach(function(img, index) {
      if (img === enterElement) {
        enterIndex = index;
      }
    });

    if (dragIndex < enterIndex) {
      enterElement.after(dragElement);
      [dragIndex, enterIndex] = [enterIndex, dragIndex];
    } else if (dragIndex > enterIndex) {
      enterElement.before(dragElement);
      [dragIndex, enterIndex] = [enterIndex, dragIndex];
    }
  },
  false
);

document.addEventListener('drop', function(event) {
  // 禁止 firefox 打开窗口
  event.preventDefault();
  event.stopPropagation();
});

题外话

关于 svgo 工具的问题,我向官方提出了 issue,官方回复是在 v1.1.0 的版本以后已经修复了,所以只要升级就能解决问题 //zxx: 感谢

asyncguo commented 4 years ago

demo

//zxx: 为何我拖了没效果出现
<div class="image-wrapper" id="imageWrapper">
  <img src="https://bookcover.yuewen.com/qdbimg/349573/1016010400" alt="小说1">
  <img src="https://bookcover.yuewen.com/qdbimg/349573/1015831596" alt="小说2">
  <img src="https://bookcover.yuewen.com/qdbimg/349573/1014032643" alt="小说3">
  <img src="https://bookcover.yuewen.com/qdbimg/349573/1015292101" alt="小说4">
</div>
<dialog id="dialogBox" class="dialog-box">
  <img src="" alt="预览" id="previewImage">
</dialog>
let imageWrapper = document.getElementById('imageWrapper')
let dialogBox = document.getElementById('dialogBox')
let previewImage = document.getElementById('previewImage')

let dragEl = null
let dragElIndex = null

imageWrapper.addEventListener('click', e => {
  const { nodeName, src } = e.target
  if (nodeName === 'IMG' && src){
      previewImage.src = src
      dialogBox.showModal()
  }
})

dialogBox.addEventListener('click', function () {
  this.close()
})

imageWrapper.addEventListener('dragstart', e => {
  // 记录拖拽元素
  dragEl = e.target
  dragElIndex = computeVmIndex(e.target)
})

imageWrapper.addEventListener('dragover', e => {
  // 1. 阻止默认动作开启drop
  e.preventDefault()
})

imageWrapper.addEventListener('drop', e => {
  e.preventDefault()
  // 2. 阻止Firefox打开新窗口
  e.stopPropagation()
  // 3. drop后判断是否为目标元素范围
  if (e.target.nodeName === 'IMG') {
    let crurrentElIndex = computeVmIndex(e.target)
    e.target.parentNode.removeChild(dragEl)
    // 4. 交换位置
    if (dragElIndex > crurrentElIndex) {
      // 相当于dragEl插入到e.target之前
      e.target.parentNode.insertBefore(dragEl, e.target)
    } else {
      // 相当于dragEl插入到e.target之后
      e.target.parentNode.insertBefore(dragEl, e.target.nextSibling)
    }
  }
})

imageWrapper.addEventListener('dragend', e => {
  dragEl = null
  dragElIndex = null
})

function computeVmIndex(element) {
  return Array.from(element.parentNode.children).indexOf(element)
}
guqianfeng commented 4 years ago

demo

//zxx: 体验下来有些奇怪
        * {
            padding: 0;
            margin: 0;
        }

        body {
            display: flex;
            height: 100vh;
            justify-content: center;
            align-items: center;
        }

        #img-list {
            display: grid;
            width: 600px;
            height: 600px;
            padding: 20px;
            border-style: solid;
            grid-template: repeat(3, 1fr) / repeat(3, 1fr);
            grid-gap: 20px;
        }

        #img-list > img {
            width: 100%;
            height: 100%;
            border: 1px solid #ccc;
            cursor: pointer;
        }

        dialog:not([open]) {
            opacity: 0;
        }

        dialog[open] {
            display: flex;
            width: 100%;
            height: 100%;
            border: none;
            justify-content: center;
            align-items: center;
            opacity: 1;
        }
<div id="img-list">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1010734492/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1010868264/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/3602691/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1010400217/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1011449273/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1011705052/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1009480992/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1013432302/150" alt="" draggable="true">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1011058239/150" alt="" draggable="true">
</div>
<dialog>
    <img src="//bookcover.yuewen.com/qdbimg/349573/1010734492/150" alt="" id="show-img">
</dialog>
    {
        let imgList = document.getElementById("img-list");
        let aImg = imgList.querySelectorAll("img");
        let showImg = document.getElementById("show-img");
        let oDialog = document.querySelector("dialog");
        let dragItem = null;
        let type = 1; //0就放在目标元素之前吧,1就互换

        imgList.addEventListener("click", (e) => {
            if(e.target.tagName === "IMG"){
                showImg.src = e.target.src;
                oDialog.show();
            }
        });

        oDialog.addEventListener("click", function(){
            this.close();
        });

        aImg.forEach(item => {
            item.addEventListener("dragstart", (e) => {
                dragItem = item;
            });
        });

        imgList.addEventListener("dragover", (e) => {
            e.preventDefault();
        });

        imgList.addEventListener("drop", (e) => {
            if(e.target != dragItem){
                if(type === 0){
                    e.target.before(dragItem);
                }else{
                    let [a, b] = [e.target.src, dragItem.src];
                    [a, b] = [b, a];
                    e.target.src = a;
                    dragItem.src = b;
                }
            }
        })
    }
frankyeyq commented 4 years ago

demo

<div class="imgs">
    <img src="images/1.jpg">
    <img src="images/2.jpg">
    <img src="images/3.jpg">
    <img src="images/4.jpg">
</div>

<dialog id="imgDialog">
    <img src="">
</dialog>
.imgs img {
    width: 100px;
    height: 100px;
    cursor: pointer;
    margin-bottom: 10px;
}
.imgs {
    width: 320px;
    font-size: 0;
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
}
#imgDialog {
    position: absolute;
    top: 0;
}
#imgDialog img{
    width: 100%;
}
var imgDialog = document.getElementById('imgDialog')
var dragElement
var dragEvent
document.addEventListener('click', event => {
    var src = event.target.src
    var dialogImg = imgDialog.querySelector('img')
    if (src && dialogImg.src !== src ) {
        imgDialog.querySelector('img').src = src
        imgDialog.showModal()
        imgDialog.setAttribute('open', true)
    } else {
        imgDialog.close()
    }
})

document.addEventListener('dragstart', event => {
    dragEvent = event
    dragElement = event.target
})

document.addEventListener('dragover', event => {
    var imgNodes = [...document.querySelectorAll('.imgs img')]
    if(imgNodes.includes(event.target)) {
        var target = event.target
        if (dragEvent.offsetX > event.offsetX) {
            target.before(dragElement)
        } else {
            target.after(dragElement)
        }
    } else {
        var firstNode = imgNodes[0]
        var lastNode = imgNodes[imgNodes.length -1]
        var lastNodeX = lastNode.offsetLeft + lastNode.offsetWidth
        var lastNodeY = lastNode.offsetTop
        if (event.offsetX > lastNodeX && event.offsetY > lastNodeY) {
            document.querySelector('.imgs').appendChild(dragElement)
        }
    }
})
silverWolf818 commented 4 years ago

示例代码忘记加了,编辑一下 预览地址

#image-box {
            width: 400px;
            display: flex;
            flex-wrap: wrap;
        }

        .image {
            margin: 0 10px 10px 0;
            display: block;
            width: calc(100% / 3 - 10px);
        }

        dialog {
            position: fixed;
            top: 50px;
        }
<div id="image-box">
    <img class="image" src="https://bookcover.yuewen.com/qdbimg/349573/1015289424/150" alt="image"/>
    <img class="image" src="https://bookcover.yuewen.com/qdbimg/349573/1015673983/150" alt="image">
    <img class="image" src="https://bookcover.yuewen.com/qdbimg/349573/1015861294/150" alt="image">
    <img class="image" src="https://bookcover.yuewen.com/qdbimg/349573/1015440199/150" alt="image">
</div>
var currentDrag = "";
var imageContainer = document.querySelector("#image-box");
var imageList = [].slice.call(document.querySelectorAll(".image"));
var dialog = document.createElement("dialog");
document.body.appendChild(dialog);

imageList.forEach(function (el) {
    el.addEventListener("click", function (e) {
        var src = e.target.src;
        dialog.innerHTML = `<img src=${src} />`;
        dialog.showModal();
    });
    el.addEventListener("dragstart", function (e) {
        currentDrag = e.target;
    });
    el.addEventListener("dragover", function (e) {
        e.preventDefault();
        if (e.target.tagName === 'IMG' && e.target !== currentDrag) {
            //使用2个元素交换法则
            const temp = document.createElement("img");
            imageContainer.replaceChild(temp, e.target);
            imageContainer.replaceChild(e.target, currentDrag);
            imageContainer.replaceChild(currentDrag, temp);
        }
    });
    el.addEventListener('dragend', function () {
        currentDrag = null;
    });
});

dialog.addEventListener("click", function (e) {
    console.log(e);
    if (e.target.nodeName === "DIALOG") {
        this.close();
    }
});
xxf1996 commented 4 years ago

预览demo

2019-11-5:修正拖放时位置没有即时变化的问题;为被拖放的图片增加效果;
看来想要更好的拖拽体验,需要去好好的研究一下DataTransfer对象才行。
//zxx: 位置变化应该是即时的
#container {
  display: flex;
  flex-flow: row wrap;
  justify-content: space-between;
  width: 375px;
}
#container>img {
  width: 30%;
  margin-bottom: 10px;
}
.dragging {
  outline: 2px dashed #666;
}
<div id="container">
  <img src="https://placekitten.com/200/200">
  <img src="https://placekitten.com/240/240">
  <img src="https://placekitten.com/280/280">
  <img src="https://placekitten.com/320/320">
</div>
let container = document.getElementById('container')
let dialog = document.createElement('dialog')
document.body.appendChild(dialog)

// 第一题
container.addEventListener('click', e => {
  if (e.target.tagName.toLowerCase() === 'img') {
    dialog.innerHTML = ''
    dialog.appendChild(e.target.cloneNode())
    dialog.showModal()
  }
})

dialog.addEventListener('click', e => {
  dialog.close()
})

// 第二题
let dragImg = null
let dropImg = null

container.addEventListener('dragstart', e => {
  if (e.target.tagName.toLowerCase() === 'img') {
    dragImg = e.target
    dragImg.classList.add('dragging') // 为拖动元素添加效果
  }
})

container.addEventListener('dragenter', e => {
  if (e.target.tagName.toLowerCase() === 'img') {
    dropImg = e.target
  }
})

container.addEventListener('dragover', e => {
  e.preventDefault()
  if (dropImg !== dragImg) {
    let imgs = Array.from(container.querySelectorAll('img'))
    let dragIdx = imgs.indexOf(dragImg) // 被拖的图片的索引
    let dropIdx = imgs.indexOf(dropImg) // 将放入的图片的索引
    if (dropIdx > dragIdx) { // 判断排序往前还是往后
      dropImg.after(dragImg)
    } else {
      dropImg.before(dragImg)
    }
  }
})

container.addEventListener('dragend', e => {
  dragImg.classList.remove('dragging')
})
sghweb commented 4 years ago

demo

//zxx: 位置变化应该是即时的
body {
  position: relative;
  margin: 0;
  padding: 0;
}
.imgbox {
  display: flex;
  flex-wrap: wrap;
  width: 510px;
  margin: 30px auto;
}
.imgbox img {
  width: 150px;
  height: 150px;
  margin: 10px 5px;
}
dialog {
  width: 100vw;
  height: 100vh;
  padding: 0;
  border: none;
  background: transparent;
}
dialog img {
  display: block;
  max-width: 80vh;
  max-height: 80vh;
  margin: 10vh auto;
}
dialog::backdrop {
  background: rgba(0, 0, 0, .6);
}
<div class="imgbox">
  <img src="https://b-ssl.duitang.com/uploads/blog/201312/04/20131204184148_hhXUT.jpeg" data-id="1" alt="">
  <img src="http://pic22.nipic.com/20120620/9644879_220135570113_2.jpg" data-id="2" alt="">
  <img src="http://pic33.nipic.com/20131008/13661616_134400215000_2.jpg" data-id="3" alt="">
  <img src="http://pic31.nipic.com/20130628/11809329_095205782183_2.jpg" data-id="4" alt="">
</div>
let dialog = document.createElement("dialog")
document.body.appendChild(dialog)
let bigimg = document.createElement("img")
dialog.appendChild(bigimg)
let imgbox = document.querySelector(".imgbox")
let img = document.querySelectorAll(".imgbox img")
let oversrc = null
let starsrc = null
let targetimg = null
img.forEach(item => {
  item.addEventListener("click", function(e) {
    bigimg.setAttribute("src", this.getAttribute("src"))
    dialog.showModal()
    e.stopPropagation()
  })
  item.addEventListener('dragstart', dragstar)
  item.addEventListener("dragend", dragEnd)
  item.addEventListener("dragover", function(e) {
    oversrc = this.getAttribute("src")
    targetimg = this
  })
})
document.addEventListener("click", function(e) {
  dialog.close()
})
function dragstar(e) {
  starsrc = this.getAttribute("src")
}
function dragEnd(e) {
  this.setAttribute("src", oversrc)
  targetimg.setAttribute("src", starsrc)
}
zengqingxiao commented 4 years ago

demo

//zxx: 图片释放会浏览器打开

HTML

  <div class="container">
    <img src="http://bookcover.yuewen.com/qdbimg/349573/1016171761/90" alt="">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1016457437/90" alt="">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1012394723/90" alt="">
    <img src="//bookcover.yuewen.com/qdbimg/349573/1016350338/90" alt="">
  </div>
  <dialog id="imgDialog" class="imgDialog">
    <img src="" alt="">
  </dialog>

CSS

   .container {
      /* border: 1px solid saddlebrown; */
      width: 310px;
      font-size: 0px;
      display: inline-block;
    }

    .container img {
      width: 100px;
      height: 100px;
      object-fit: cover;
      margin-left: 2px;
    }

    .container img:hover {
      margin-left: 0px;
      border-left: 2px solid #83D0F2 !important;
    }

    .imgDialog {
      display: flex;
      padding: 1em;
      box-sizing: border-box;
      position: fixed;
      justify-content: center;
      align-items: center;
      right: 0;
      border: 0;
      left: 0;
      top: 0;
      width: 100%;
      height: 100%;
      background: rgba(0, 0, 0, .6);
      font-size: 20px;
      color: #fff;
      transition: .3s;
    }

    .imgDialog:not([open]) {
      display: flex;
      visibility: hidden;
      opacity: 0;
    }

    .imgDialog:not([open]) img {
      /* transition: .3s; */
      transform: scale(.3);
    }

    .imgDialog[open] {
      /* transition: .3s; */
      visibility: visible;
      opacity: 1;
    }

    .imgDialog[open] img {
      transform: scale(1);
    }

    .imgDialog img {
      transition: .3s;
      height: 60%;
      object-fit: contain;
    }

JS

   let imgDoalogEle = document.getElementById('imgDialog')
    let imgDoalogImgEle = document.querySelector('.imgDialog img')
    let container = document.querySelector('.container')
    console.log(imgDoalogEle, 'dialog')
    let containerImg = [].slice.call(document.querySelectorAll('.container img'))
    console.log(containerImg)
    containerImg.forEach(item => {
      item.addEventListener('click', () => {
        imgDoalogImgEle.src = item.src
        imgDoalogEle.show()
      })
    });
    imgDoalogEle.addEventListener('click', function (ev) {
      if (ev.target.tagName === 'DIALOG') {
        this.close();
      }
    })

    // 第二题
    let sedTarget
    let sedTargetSite
    let endTarget
    let endTargetSite
    function computeVmIndex(element) {
      return Array.from(element.parentNode.children).indexOf(element)
    }

    // 当拖动元素或选中的文本到一个可释放目标时触发
    container.addEventListener('dragstart', (ev) => {
      if (ev.target.tagName === 'IMG') {
        // console.log(computeVmIndex(ev.target))
        sedTargetSite = computeVmIndex(ev.target)
        sedTarget = ev.target;
        ev.target.style.opacity = .5;
      }
    })

    // 当拖拽操作结束时触发 (比如松开鼠标按键或敲“Esc”键).
    container.addEventListener("dragend", function (ev) {
      if (ev.target.tagName === 'IMG') {
        // reset the transparency
        ev.target.style.opacity = "";
      }
    }, false);

    // 当元素或选中的文本被拖到一个可释放目标上时触发
    /* events fired on the drop targets */
    container.addEventListener("dragover", function (ev) {
      if (ev.target.tagName === 'IMG') {
        // console.log('当元素或选中的文本被拖到一个可释放目标上时触发', computeVmIndex(event.target))
        ev.target.style.borderLeft = '2px solid #83D0F2'
        // prevent default to allow drop
        event.preventDefault();
      }
    }, false);

    // 当拖动元素或选中的文本离开一个可释放目标时触发
    container.addEventListener("dragleave", function (ev) {
      if (ev.target.tagName === 'IMG') {
        ev.target.style.border = 'none'
      }
    })

    // 当元素或选中的文本在可释放目标上被释放时触发
    container.addEventListener("drop", ev => {
      if (ev.target.tagName === 'IMG') {
        ev.target.style.border = 'none'
        endTargetSite = computeVmIndex(ev.target) // 目标地址
        console.log(computeVmIndex(ev.target))

        if (sedTargetSite > endTargetSite) {
          ev.target.parentNode.insertBefore(sedTarget, ev.target)
        } else {
          ev.target.parentNode.insertBefore(sedTarget, ev.target.nextSibling)
        }
      }
    })
Seasonley commented 4 years ago

demo

//zxx: 弹框效果有些奇怪。拖拽会有穷闪场景。
var $container = document.getElementById("container"),// 容器
    $win_perview = document.getElementById("win_perview"),// 预览窗体
    $t = {},// 被点击的图
    $shadow,// 随鼠标移动的图
    $boxs;//图
function clickHandler(e) {// 点击事件
    if (e.target instanceof HTMLImageElement) {
        $win_perview.style.setProperty("--src", `url(${e.target.src})`);
        $win_perview.open = "open";
    }
}
function draggerHandler(e) {
    e.preventDefault();
    e.stopPropagation();// 防止浏览器插件冲突
    switch (e.type) {
        case "mousedown":
            if (e.target instanceof HTMLImageElement) {
                var { top, left, width, height } = e.target.getBoundingClientRect();
                $boxs = getGravityPoints($container.children);// 缓存所有元素中心
                $t = {//缓存被点击的图
                    target: e.target,
                    top,left,height,width,
                    gX: top + height / 2,
                    gY: left + width / 2,
                };
                $shadow = $t.target.cloneNode();// 缓存随鼠标移动的图
                [
                    ["position", "fixed"],
                    ["margin", "0"],
                    ["left", $t.left + "px"],
                    ["top", $t.top + "px"],
                    ["pointerEvents", "none"],
                    ["width", $t.width + "px"]
                ].forEach(([k, v]) => $shadow.style[k] = v);
                document.body.append($shadow);
                $t.target.style.opacity = 0.2;
                $container.addEventListener("mousemove", draggerHandler);
                document.addEventListener("mouseup", draggerHandler);
            }
            break;
        case "mousemove":
            if (!$t.moved) {$t.moved = true;}
            var { left, top } = $shadow.style;
            [left, top] = [parseInt(left) + e.movementX, parseInt(top) + e.movementY];
            //将插入的位置 nextIdx; 之前的位置 prevIdx;
            var nextIdx = getCloserIndex($boxs, left + $t.width / 2, top + $t.height / 2);
            var prevIdx = getElementIndex($t.target);
            // 换位
            if (nextIdx + 1 === $container.children.length) {
                $container.appendChild($t.target)
            }
            else if (prevIdx + 1 === nextIdx) {
                $container.insertBefore($t.target, $container.children[nextIdx + 1]);
            }
            else if ($t.target !== $container.children[nextIdx]) {
                $container.insertBefore($t.target, $container.children[nextIdx]);
            }
            $boxs = getGravityPoints($container.children);// 重新缓存
            // 位移随鼠标移动的图
            // $shadow.style.left = left + "px";
            // $shadow.style.top = top + "px";
            $shadow.style.left = e.clientX - $t.width / 2 + "px";
            $shadow.style.top = e.clientY - $t.height / 2 + "px";
            break;
        case "mouseup":
            !$t.moved && clickHandler(e); //没移动则触发点击
            $t.target.style.opacity = 1;
            $t = null;
            $container.removeEventListener("mousemove", draggerHandler);
            document.removeEventListener("mouseup", draggerHandler);
            $shadow.parentNode.removeChild($shadow);
            break;
    }
}

function getElementIndex(ele) {// 获取元素索引
    for (var i = 0; ele = ele.previousElementSibling; i++);
    return i;
}

function getGravityPoints([...children]) {// 获取所有元素中心
    return children.map(item => {
        var { top, left, width, height } = item.getBoundingClientRect();
        return {
            gX: left + width / 2,
            gY: top + height / 2
        };
    });
}

function getCloserIndex($boxs, X, Y) {// 判定放置点
    var point = $boxs.map(({ gX, gY }) => parseInt(Math.hypot(X - gX, Y - gY)));
    var i = point.reduce(
        (acc, cur, idx) => {
            return acc.v > cur ? { i: idx, v: cur } : acc;
        },
        { i: 0, v: point[0] }
    ).i;
    return i;
}
$win_perview.addEventListener("click", $win_perview.close);
$container.addEventListener("mousedown", draggerHandler);
NeilChen4698 commented 4 years ago

Demo https://codepen.io/crazyboy/pen/xxxpbry

//zxx: 点击弹框位置隐藏;位置变化应即时
(function () {
    let dialogEle = document.createElement('dialog');
    dialogEle.style.cssText = 'box-shadow: 0 0 0 100vmax rgb(0,0,0,0.3); z-index: 1;'
    let dialogImgEle = document.createElement('img');
    dialogEle.appendChild(dialogImgEle);
    document.body.appendChild(dialogEle);
    document.body.onclick = function(e) {
        if (e.target.nodeName === 'IMG' && e.target.parentNode.nodeName !== 'DIALOG') {
            dialogImgEle.src = e.target.attributes.src.value;
            dialogEle.setAttribute('open', 'open');
        } else {
            dialogEle.removeAttribute('open');
        }
    };
    let registerDrag = function(ele) {
        ele.setAttribute('draggable', 'true');
        let parent = ele.parentNode;
        ele.ondragend = function(e) {
            let exchangeEle;
            let sibling = e.target.nextElementSibling;
            for (let i = 0; i < parent.children.length; i++) {
                let child = parent.children[i];
                if (child.offsetLeft <= e.pageX && e.pageX <= (child.offsetLeft + child.offsetWidth) && child.offsetTop <= e.pageY && e.pageY <= (child.offsetTop + child.offsetHeight)) {
                    exchangeEle = parent.children[i];
                    break;
                }
            }
            if (!!exchangeEle && exchangeEle !== e.target) {
                if (exchangeEle === sibling) {
                    parent.removeChild(exchangeEle);
                    parent.insertBefore(exchangeEle, e.target);
                } else {
                    parent.removeChild(e.target);
                    parent.insertBefore(e.target, exchangeEle);
                    parent.removeChild(exchangeEle);
                    if (!!sibling) {
                        parent.insertBefore(exchangeEle, sibling);
                    } else {
                        parent.appendChild(exchangeEle);
                    }
                }
            }
        }
    };
    document.querySelector('div').style.cssText = 'width: 300px;'
    Array.from(document.querySelectorAll('div img')).forEach(v => {
        v.style.cssText = 'float: left; width: 30%; padding: 0 1.5% 3% 1.5%;';
        registerDrag(v);
    });
})();
ziven27 commented 4 years ago

在线demo JSBIN

//zxx: 弹框实现不错,拖拽存在相邻无法互换问题
.imgs {
    font-size: 0;
    max-width: 312px;
}

.imgs img {
    width: 100px;
    height: 100px;
    margin: 2px;
    cursor: pointer;
    transition: transform 300ms;
}
.imgs img._on{
    outline: 2px dashed red;
    opacity: 0.5;
}

.imgs img:hover {
    transform: scale(1.1);
}

.imgs img:active {
    transform: scale(0.98);
}

.img-dialog {
    padding: 0;
    margin: 0;
    width: 100%;
    height: 100%;
    border: none 0;
    cursor: pointer;
}

.img-dialog[open] {
    background: transparent url('data:image/gif;base64,R0lGODlhHwAfANUAAN3d3dXV1dPT09vb2+rq6vHx8efn5/Ly8uzs7P39/c3Nzejo6NHR0c7OztLS0uLi4tra2tTU1NjY2NfX197e3vb29tDQ0NnZ2eTk5Nzc3O/v7+np6dbW1t/f3/Dw8OHh4eDg4OXl5fz8/Ovr6+Pj4/T09O3t7fn5+fr6+ubm5vPz8/v7+/j4+O7u7vX19ff398/Pz////8zMzAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wTU09Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9tbS8iIHhtbG5zOnN0UmVmPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvc1R5cGUvUmVzb3VyY2VSZWYjIiB4bWxuczp4bXA9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC8iIHhtcE1NOk9yaWdpbmFsRG9jdW1lbnRJRD0ieG1wLmRpZDowNzgwMTE3NDA3MjA2ODExOEMxNERCODM5NjcyODU1MiIgeG1wTU06RG9jdW1lbnRJRD0ieG1wLmRpZDpBNTI1OERFQkMyNDkxMUUwOTcyOEE4MDlGQzk4Njk1MiIgeG1wTU06SW5zdGFuY2VJRD0ieG1wLmlpZDpBNTI1OERFQUMyNDkxMUUwOTcyOEE4MDlGQzk4Njk1MiIgeG1wOkNyZWF0b3JUb29sPSJBZG9iZSBQaG90b3Nob3AgQ1M1IE1hY2ludG9zaCI+IDx4bXBNTTpEZXJpdmVkRnJvbSBzdFJlZjppbnN0YW5jZUlEPSJ4bXAuaWlkOjA5ODAxMTc0MDcyMDY4MTE4QzE0REI4Mzk2NzI4NTUyIiBzdFJlZjpkb2N1bWVudElEPSJ4bXAuZGlkOjA3ODAxMTc0MDcyMDY4MTE4QzE0REI4Mzk2NzI4NTUyIi8+IDwvcmRmOkRlc2NyaXB0aW9uPiA8L3JkZjpSREY+IDwveDp4bXBtZXRhPiA8P3hwYWNrZXQgZW5kPSJyIj8+Af/+/fz7+vn49/b19PPy8fDv7u3s6+rp6Ofm5eTj4uHg397d3Nva2djX1tXU09LR0M/OzczLysnIx8bFxMPCwcC/vr28u7q5uLe2tbSzsrGwr66trKuqqainpqWko6KhoJ+enZybmpmYl5aVlJOSkZCPjo2Mi4qJiIeGhYSDgoGAf359fHt6eXh3dnV0c3JxcG9ubWxramloZ2ZlZGNiYWBfXl1cW1pZWFdWVVRTUlFQT05NTEtKSUhHRkVEQ0JBQD8+PTw7Ojk4NzY1NDMyMTAvLi0sKyopKCcmJSQjIiEgHx4dHBsaGRgXFhUUExIREA8ODQwLCgkIBwYFBAMCAQAAIfkEBAAAAAAsAAAAAB8AHwAABv/AmHBIPBgAAZgMFgAYDsSoVJhADGTYrFY2QCSm0gJgS9YCCuAhQVBuYwWE9ELpdsMWUwK9bo8TC2x8fAJoVGOCggBfMQhuASQeJ4gyCEJXZRQHKyIiWQp1AzEHbQAVJxsDFp4NdUZlER4sD2Uwe2RHZSAVBm0NDKxlSGUjFRdtCg4MbUllBSW2WwKBZNBZHirVWQERdmULBRNtMBPTW0yYHrOvEsplTWUOGy0ZZAIDE8BkTm0XIyMdARQgk0BhQLsyT9xICLFhQQoMD0AMcNAqxqUyDCCAgJiBQ7YsoRhNQlQpRoJDI0ktigEoZRlCUfS41ALDT5Q5M5fgAbPGJZwZNELEjDwDdEiVi226rCw6xAgSJUycQEkTBAAh+QQEAAAAACwIAAgADgAOAAAGb0CZUABCuCotUkDIHLSOrBNqpeowIYVS6AKDQQgi1HB0sDKFGCbAQzq7hSHN5M1UyEamBl3YaGwQentdDwRLdAoWXhsUgg4NDA8pEm8WEQxCHBgYEA4KiAIcATBMAR0PIBQZEBKibjAcEBkDE51MQQAh+QQEAAAAACwIAAgADgAOAAAGc0CZ0AEwaDwEUETIlCyOBVWpogE0CabPBAaTpF4ViowRIgyYzM/pELhsxGjmBkUCGZZxIWSlwYQUeUIwIicPGICBTBkPAolMHB8SjoAwGRR4aAoNiAIZGQEwQgowFqFMDhIQEhMBAg4WiEwNDgEcEQymQkEAIfkEBAAAAAAsCAAIAA4ADgAABnVAmZABeWwIIYBAyOQ8FpuRqaUhQJgBUjLQaExIhcJAButgJEwmRWUSOK9pZqgCgoAcceblhQBQFHlCMCwuGQCAgTAnFRMDFoEyECgaAhARkAQiJA0cEgyBCSUBMhYcHAyIaR1MMAIRDgwwDalpCjAWFjC0MkEAIfkEBAAAAAAsCAAIAA4ADgAABnRAmRAWyHwenQtDyHQMOkdMyICZMBkDwMShUEQAm41E1pAMBEwmZGRwOC6BdPrTAkQkMDlzojEErHpDBRoRcYEyMCoeDhEKhxIlBDAOeYEGFSAyFgyOcg8sHhEyCjAwnTIDGygVAGkNXUIiKwcUh0IeJIYyQQAh+QQEAAAAACwIAAgADgAOAAAGcUCZUOHgQAaXAEzIhAUkF0iG0gFEmgGOYKlgSB6PgNARsTCZE8zH0nCYz8xM6gJjKODMyOIDe+NlDQQLMEt/gAgbCg2GMhMmITJ3hiQaAIwyHQUEAmcjA4QXKSUFEGclIisoJywVLQNwAZQVFSYfnEJBACH5BAQAAAAALAgACAAOAA4AAAZyQJlQ1mAIIgKGYihUwBiOSGAimTCYMAtsKYMJIBBHs8FlCjIQmExRZsomlIB7zgAN5m7F44NnKjAPHiJ3eBEpHRgiBH0UCxcBKigPcwMEGFcULycGFzAwEw8mBBJDFAUvFSUqBRoLpEwRHyMFHgsUYkJBACH5BAQAAAAALAgACAAOAA4AAAZvQJlQqGjAYI2hkmi0MBhJYWcpU8AEApgsUBJRZbBAREESEb4yx4ShWQ3QMAmngtKiIZfKyU5VZAYmLBBoDBQXHxUGaBcgYhouU0sTDxRaGQcHGBMNDQEAIQ8BQwMjBRomCAQLoUsCFCkjGw8QDENBADs=') 50% 50% no-repeat;
}

.img-detail {
    position: fixed;
    top: 16px;
    right: 16px;
    bottom: 16px;
    left: 16px;
    background-position: 50% 50%;
    background-repeat: no-repeat;
    background-size: contain;
    transition: transform 200ms;
}

.img-detail:active {
    transform: scale(0.98);
}

.img-dialog::backdrop {
    background-color: rgba(0, 0, 0, 0.8);
}
<div class="imgs j_imgs" id="imgThumbs">
    <img draggable="true" width="100" height="100" src="https://placem.at/people?txt=1&w=100&h=100&random=5"
        data-src="https://placem.at/people?txt=1&w=500&h=500&random=5" alt="img" title="显示大图1">
    <img draggable="true" width="100" height="100" src="https://placem.at/people?txt=2&w=100&h=100&random=2"
        data-src="https://placem.at/people?txt=2&w=500&h=500&random=2" alt="img" title="显示大图2">
    <img draggable="true" width="100" height="100" src="https://placem.at/people?txt=3&w=100&h=100&random=3"
        data-src="https://placem.at/people?txt=3&w=500&h=500&random=3" alt="img" title="显示大图3">
    <img draggable="true" width="100" height="100" src="https://placem.at/people?txt=4&w=100&h=100&random=4"
        data-src="https://placem.at/people?txt=4&w=500&h=500&random=4" alt="img" title="显示大图4">
</div>
<dialog class="img-dialog" id="imgDialog" title="关闭">
    <div class="img-detail" id="imgDetail"></div>
</dialog>
// 第一题 
var eleImgDialog = document.getElementById('imgDialog');
var eleImgDetail = document.getElementById('imgDetail');
var eleThumbBox = document.getElementById('imgThumbs');
eleImgDialog.addEventListener('click', function (e) {
    eleImgDialog.close();
    eleImgDetail.style.backgroundImage = null;
});
eleThumbBox.addEventListener('click', function (e) {
    e.preventDefault();
    if (e.target && e.target.tagName === "IMG") {
        var originSrc = e.target.dataset.src;
        eleImgDetail.style.backgroundImage = 'url("' + originSrc + '")';
        eleImgDialog.showModal();
    }
});

// 第二题 drap 事件 
var eleCurrentDrop=null;
eleThumbBox.addEventListener('dragover',function(e){
    e.preventDefault();
});

eleThumbBox.addEventListener('dragenter',function(e){
    var eleOn = eleThumbBox.querySelector('._on');
    eleOn && eleOn.classList.remove("_on");
    e.target.classList.add("_on");
});
eleThumbBox.addEventListener('dragend',function(e){
    if(eleCurrentDrop){
        eleThumbBox.insertBefore(e.target,eleCurrentDrop);
        eleCurrentDrop=null;
    }
    var eleOn = eleThumbBox.querySelector('._on');
    eleOn && eleOn.classList.remove("_on");
});
eleThumbBox.addEventListener('drop',function(e){
    eleCurrentDrop = e.target;
});
JaimeCheng commented 4 years ago

>在线demo<

//zxx: 拖拽会变成打开图片
.img-list {
  width: 260px;
  display: flex;
  justify-content: space-between;
  flex-wrap: wrap;
}
.img-item {
  width: 83px;
  height: 83px;
  display: block;
  margin-bottom: 5px;
  object-fit: cover;
  cursor: move; 
}
.img-preview {
  max-width: 100%;
  height: auto;
  display: block;
  margin-bottom: 10px;
}
dialog::backdrop {
  background: rgba(0, 0, 0, .4);
}
<div class="img-list">
  <img class="img-item" draggable="true" src="https://bookcover.yuewen.com/qdbimg/349573/1016562761" alt="">
  <img class="img-item" draggable="true" src="http://bookcover.yuewen.com/qdbimg/349573/1016594554" alt="">
  <img class="img-item" draggable="true" src="http://bookcover.yuewen.com/qdbimg/349573/1016706397" alt="">
  <img class="img-item" draggable="true" src="http://bookcover.yuewen.com/qdbimg/349573/1016673054" alt="">
  <img class="img-item" draggable="true" src="http://bookcover.yuewen.com/qdbimg/349573/1016446189" alt="">
  <img class="img-item" draggable="true" src="http://bookcover.yuewen.com/qdbimg/349573/1016232348" alt="">
</div>

<dialog id="img-box">
  <img class="img-preview" src="" alt="">
</dialog>
const dialog = document.getElementById('img-box')
const imgItems = document.querySelectorAll('.img-item')
const imgPreview = document.querySelector('.img-preview')
var dragEl = null 

imgItems.forEach(item => {
  const src = item.getAttribute('src')
  item.onclick = function () {
    imgPreview.setAttribute('src', src)
    dialog.showModal()
  }
  item.addEventListener('dragstart', handleStart, false)
  item.addEventListener('dragover', handleOver, false)
  item.addEventListener('drop', handleDrop, false)
})

dialog.onclick = function () {
  dialog.close()
}

function handleStart (e) {
  dragEl = this
  e.dataTransfer.effectAllowed = 'move'
  e.dataTransfer.setData('Text', e.target.src)
}
function handleOver (e) {
  if (e.preventDefault) {
    e.preventDefault()
  }
  e.dataTransfer.dropEffect = 'move'
  return false
}
function handleDrop(e) {
  if (e.stopPropagation) {
    e.stopPropagation()
  }
  if (dragEl != this) {
    dragEl.src = this.src
    this.src = e.dataTransfer.getData('Text')
  }
  return false
}
zy017 commented 4 years ago

demo

<div class="drag-container">
    <img draggable data-src="https://bookcover.yuewen.com/qdbimg/349573/1010734492/300"
        src="https://bookcover.yuewen.com/qdbimg/349573/1010734492/150">
    <img draggable data-src="https://bookcover.yuewen.com/qdbimg/349573/1010868264/300"
        src="https://bookcover.yuewen.com/qdbimg/349573/1010868264/150">
    <img draggable data-src="https://bookcover.yuewen.com/qdbimg/349573/3602691/300"
        src="https://bookcover.yuewen.com/qdbimg/349573/3602691/150">
    <img draggable data-src="https://bookcover.yuewen.com/qdbimg/349573/1010400217/600"
        src="https://bookcover.yuewen.com/qdbimg/349573/1010400217/150">
</div>
.drag-container {
    width: 500px;
}

.drag-container img {
    cursor: pointer;
    width: 150px;
    object-fit: cover;
    transition: all 0.5s;
    margin: 5px;
}
// 1.
function showDialog(dom) {
    var dialog
    if (document.querySelector('dialog[data-use=imgPreview]')) {
        dialog = document.querySelector('dialog[data-use=imgPreview]')
        dialog.innerHTML = ''
    } else {
        dialog = document.createElement('dialog')
        dialog.dataset.use = "imgPreview"
        dialog.style.position = "fixed"
        dialog.style.left = 0
        dialog.style.right = 0
        dialog.style.bottom = 0
        dialog.style.top = 0
        document.body.appendChild(dialog)
        dialog.addEventListener('click', (e) => {
            dialog.close()
        })
    }
    dialog.appendChild(dom)
    dialog.showModal()
}
document.querySelector(".drag-container").addEventListener('click', (e) => {
    let self = e.target
    if (self.tagName === 'IMG') {
        let img = document.createElement('img')
        img.style.maxWidth = "80vw"
        img.style.maxHeight = "80vh"
        img.src = self.dataset.src
        showDialog(img)
    }
})
// 2.
function dragSort(dom) {
    var dragged
    dom.addEventListener("dragstart", function (e) {
        dragged = e.target
        // 使其透明
        e.target.style.opacity = 0
    }, false)

    dom.addEventListener("dragend", function (e) {
        // 重置透明度
        e.target.style.opacity = ""
    }, false)

    dom.addEventListener("dragover", function (e) {
        // 阻止默认动作
        e.preventDefault()
        if (e.target.tagName == "IMG") {
            let parent = e.target.parentNode
            let next = dragged.nextSibling
            parent.insertBefore(dragged, e.target)
            parent.insertBefore(e.target, next)
        }
    }, false)
}
dragSort(document.querySelector(".drag-container"))
livetune commented 4 years ago

感觉有少许bug demo

//zxx: 点击任意位置隐藏弹框;拖拽动画有些多余。
.img-list {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-evenly;
  width: 400px;
  border: 1px solid #eeeeee;
  margin: auto;
  cursor: pointer;
}

.pad {
  width: 30%;
}

.img-item {
  width: 30%;
  margin: 1.25% 0;
  border: 1px solid #ececec;
  box-sizing: border-box;
  transition: width .2s linear;
}

dialog img {
  width: 100%;
  transition: all 2s linear;
  cursor: pointer;
  user-select: none;
}

dialog {
  width: 40vw;
}
<div class="img-list">
  <img class="img-item" src="http://b-ssl.duitang.com/uploads/item/201610/12/20161012010005_dHfu8.jpeg" alt="">
  <img class="img-item"
    src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1572541328333&di=68f44f6742fe3f5dc860b6de5be7b591&imgtype=0&src=http%3A%2F%2Fimg.zcool.cn%2Fcommunity%2F019a8058464281a8012060c822b94f.jpg"
    alt="">
  <img class="img-item"
    src="https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1573223264&di=5608a4a1e45db8c28cd81330e6456495&imgtype=jpg&er=1&src=http%3A%2F%2Fi1.hdslb.com%2Fbfs%2Fface%2F476d919128aa86b46b6b192d1a0c33dde6a8db03.jpg"
    alt="">
  <img class="img-item"
    src="https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=1540585939,4136794415&fm=26&gp=0.jpg" alt="">
  <i class="pad"></i>
  <i class="pad"></i>
  <i class="pad"></i>
</div>
<dialog><img src="" alt=""></dialog>
const dialog = document.querySelector('dialog')
const imglist = document.querySelector('.img-list')
let dragTarget
document.querySelectorAll('.img-item').forEach(item => {
 // 第二题
  item.addEventListener('dragstart', dragstart)
  item.addEventListener('dragend', dragend)
  item.addEventListener('dragenter', dragenter)
  // 第一题
  item.addEventListener('click', (e) => {
    dialog.querySelector(':root img').src = e.target.src
    document.querySelector('dialog').showModal()
    setTimeout(() => {
      window.addEventListener('click', closeDialog)
    })
  })
})
document.querySelector('dialog img').addEventListener('click', e => {
  e.stopPropagation()
})
function closeDialog() {
  dialog.close()
  window.removeEventListener('click', closeDialog)
}
function dragstart(event) {
  dragTarget = event.target
  event.target.style.opacity = '0'
}
function dragenter(e) {
  const target = event.target
  if (target !== dragTarget) {
    dragTarget.style.width = '0'
    console.log(target)
    if (target.previousElementSibling === dragTarget) {
      imglist.insertBefore(dragTarget, target.nextElementSibling)
    } else {
      imglist.insertBefore(dragTarget, target)
    }
  }
  setTimeout(() => {
    dragTarget.style.width = ''
  })
}

function dragend(event) {
  dragTarget.style.opacity = '1'
  const last = imglist.querySelector('.img-item:last-of-type')
  if (event.pageX > (last.offsetLeft + last.clientWidth), event.pageY > last.offsetTop) {
    imglist.insertBefore(dragTarget, imglist.querySelector('.pad:first-of-type'))
  }
}
wingmeng commented 4 years ago

> Demo <

//zxx: 拖不动,除了拖到最后

wingmeng: 多谢张老师,昨晚太困了 js 逻辑没写完 :cry: ,今早才补完的

.img-list {
  display: flex;
  flex-wrap: wrap;
  width: 260px;
  background: #fff;
  border: 1px solid #ccc;
  transition: all .2s;
}

.img-list.is-dragenter {
  background: #ffd;
  border-color: #333;
}

.img-list > img {
  max-width: 33.3333%;
  padding: 2px;
  box-sizing: border-box;
  cursor: move;
}

.img-preview img {
  display: block;
  max-width: 100%;
}
.img-preview::backdrop {background: rgba(0, 0, 0, .6);}
<div class="img-list" id="imgList">
  <img src="https://cdn.pixabay.com/photo/2014/12/30/13/19/girls-583917_960_720.jpg" alt="海边女孩">
  <img src="https://cdn.pixabay.com/photo/2019/10/21/10/33/garden-4565700_960_720.jpg" alt="花园">
  <img src="https://cdn.pixabay.com/photo/2019/10/27/18/48/chinatown-4582511_960_720.jpg" alt="都市">
  <img src="https://cdn.pixabay.com/photo/2016/12/03/14/20/woman-1879905_960_720.jpg" alt="金发女郎">
  <img src="https://cdn.pixabay.com/photo/2019/10/08/18/13/matterhorn-4535693_960_720.jpg" alt="山峰">
  <img src="https://cdn.pixabay.com/photo/2017/04/22/10/15/sport-2250970_960_720.jpg" alt="运动">
  <img src="https://cdn.pixabay.com/photo/2013/06/10/09/23/morocco-123978_960_720.jpg" alt="沙漠">
</div>
const viewImgList = {
  init(selector) {
    this.el = document.querySelector(selector);
    this.dialog = null;
    this.bindClick();
    this.bindDrag();
  },
  bindClick() {
    // 图片点击事件
    this.el.addEventListener('click', e => {
      if (e.target.tagName.toLowerCase() === 'img') {
        this.togglePreview(e.target.src);
      }
    });

    // 任意位置点击
    window.addEventListener('click', e => {
      if (!this.el.contains(e.target)) {
        this.togglePreview(false);
      }
    });
  },
  bindDrag() {
    let imgElm = null;
    const isInside = elm => elm === this.el || this.el.contains(elm);

    // 拖动开始
    this.el.addEventListener('dragstart', e => {
      e.target.style.opacity = 0;
      imgElm = e.target;
    });

    // 拖动结束
    this.el.addEventListener('dragend', e => e.target.style.opacity = '');

    // 放置目标
    document.addEventListener('dragover', e => {
      if (isInside(e.target)) {
        e.preventDefault();

        if (e.target === this.el) {
          this.el.appendChild(imgElm);
        } else {
          const compareValue = imgElm.compareDocumentPosition(e.target);

          if (compareValue === 2) {  // 目标图片在前
            e.target.before(imgElm);  // 添加到目标图片前面
          } else if (compareValue === 4) {  // 目标对象在后
            e.target.after(imgElm);  // 添加到目标图片后面
          }
        }
      }
    });

    // 进入目标区域
    document.addEventListener('dragenter', e => {
      this.el.classList[isInside(e.target) ? 'add' : 'remove']('is-dragenter');
    });

    // 允许放置
    this.el.addEventListener('drop', e => {
      e.preventDefault();
      this.el.classList.remove('is-dragenter');
    });
  },
  togglePreview(src) {
    if (src) {
      this.buildDialog(src);
      this.dialog && !this.dialog.open && this.dialog.showModal();          
    } else {
      this.dialog && this.dialog.close();
      this.destoryDialog();
    }
  },
  buildDialog(imgSrc) {
    const dialog = document.createElement('dialog');
    const img = new Image();

    dialog.className = 'img-preview';
    img.src = imgSrc;
    this.dialog = dialog;
    dialog.appendChild(img);
    document.body.appendChild(dialog);
  },
  destoryDialog() {
    this.dialog && document.body.removeChild(this.dialog);
    this.dialog = null;
  }
};

viewImgList.init('#imgList');
GCGligoudan commented 4 years ago

先打个卡,明天早起来做

kuikuiGe commented 4 years ago

demo

<div class="img-list">
    <img class="img-item" src="https://img.alicdn.com/imgextra/i4/3015214310/O1CN01OFcLG71hi1c3MRrLi_!!0-item_pic.jpg_60x60q90.jpg" data-big="https://img.alicdn.com/imgextra/i4/3015214310/O1CN01OFcLG71hi1c3MRrLi_!!0-item_pic.jpg_430x430q90.jpg">
    <img class="img-item" src="https://img.alicdn.com/imgextra/i2/3015214310/O1CN01eZhuJs1hi1ay8z3ti_!!3015214310.jpg_60x60q90.jpg" data-big="https://img.alicdn.com/imgextra/i2/3015214310/O1CN01eZhuJs1hi1ay8z3ti_!!3015214310.jpg_430x430q90.jpg">
    <img class="img-item" src="https://img.alicdn.com/imgextra/i2/3015214310/O1CN01wydRvC1hi1antaUhp_!!3015214310.jpg_60x60q90.jpg" data-big="https://img.alicdn.com/imgextra/i2/3015214310/O1CN01wydRvC1hi1antaUhp_!!3015214310.jpg_430x430q90.jpg">
    <img class="img-item" src="https://img.alicdn.com/imgextra/i2/3015214310/O1CN013la2Oc1hi1awuC1Ha_!!3015214310.jpg_60x60q90.jpg" data-big="https://img.alicdn.com/imgextra/i2/3015214310/O1CN013la2Oc1hi1awuC1Ha_!!3015214310.jpg_430x430q90.jpg">
</div>
let imgDialog
let imgList = document.querySelector('.img-list')
let imgItems = document.querySelectorAll('.img-item')
let source,target
let sourceIndex,targetIndex

//1. 预览弹框-打开
imgItems.forEach(function(item){
    (function(ele){
        ele.addEventListener('click',function(e){
            event.stopPropagation()
            let bigImg = e.target.getAttribute('data-big')
            openPreview(bigImg)
        })
    }(item))
})

function openPreview(url){
    if(!imgDialog){
        imgDialog = document.createElement('dialog')
        imgDialog.className = 'img-dialog'
        let bigImg = document.createElement('img')
        imgDialog.appendChild(bigImg)
        document.body.appendChild(imgDialog)
    }

    imgDialog.querySelector('img').setAttribute('src',url)

    imgDialog.show()
}

//1. 预览弹框-关闭
document.addEventListener('click',function(e){
    if(imgDialog&&imgDialog.open) {
        if(!imgDialog.contains(e.target)){
            imgDialog.close()
        }
    }
})

imgList.addEventListener('dragstart',startDrag,false)

imgList.addEventListener('dragover', function (e) {
    e.preventDefault()
}, false)

imgList.addEventListener("dragenter", function (e) {
    if (e.target.className === 'img-item') {
        e.target.classList.add('enter');
    }
    target = e.target
    targetIndex = [].slice.call(document.querySelectorAll('.img-item')).indexOf(e.target)
}, false);

imgList.addEventListener("dragleave", function (event) {
    if (/enter/.test(event.target.classList)) {
        event.target.classList.remove('enter')
    }
}, false);

imgList.addEventListener('drop', exchangeEle, false)

function startDrag(e) {
    source = e.target
    sourceIndex = [].slice.call(document.querySelectorAll('.img-item')).indexOf(e.target)
}

//2. 拖拽小图改变顺序
function exchangeEle(e) {
    e.preventDefault();

    if(targetIndex < 0){
        return
    }

    event.target.classList.remove('enter')

    source.remove()
    if(sourceIndex < targetIndex){
        target.after(source)
    }else{
        target.before(source)
    }
}
zhangxinxu commented 4 years ago

本期要点:

  1. dialog元素实现弹框的好处:语义化(辅助设备识别),键盘访问(例如ESC退出),showModal时候天然顶级层级(不支持动画?)。Firefox /IE不支持,可以写个polyfill,使用dialog元素按照传统组件开发就好了。
  2. 不推荐图片占位使用 <img src=""> ,这是糟糕的做法,会有一个错误的请求,(测试了下,现在Chrome不会了,最近刚调整的策略),推荐做法:<img>
  3. 拖拽最佳实践一是是原生的drag/drop事件,兼容性很可以。我的博客有一些案例和教程:https://www.zhangxinxu.com/wordpress/?s=drag 如果想要对拖拽,或者剪切板事件有更深入的使用的,DataTransfer对象,可以改变拖拽时候显示的图形,改变拖拽(例如文本)或者剪切板内容。
  4. 一些DOM检测与变换的算法:compareDocumentPosition/before/after(insertBefore兼容性更好),replaceChild交换算法。拖拽排序核心其实就几行代码。
  5. 比较OK的交互方式:① 即时位置变化;② dragover时候UI变化(例如增加outline虚框);