<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Document</title> <style> * { margin: 0; padding: 0; box-sizing: border-box; } .container { width: 200px; margin: 0 auto; display: flex; flex-wrap: wrap; } .container div { width: 50px; height: 50px; } </style> </head> <body> <button id="btn_add">添加</button> <div class="container"></div> <script> let divs = null; const btnAdd = document.querySelector("#btn_add"); const container = document.querySelector(".container"); const data = [ { id: 1, c: "red" }, { id: 2, c: "pink" }, { id: 3, c: "purple" }, { id: 4, c: "orange" }, { id: 4, c: "yellow" }, ]; // 立即执行函数, 初始渲染 页面的 5 个彩色盒子 (function render() { let html = ""; for (let i = 0; i < data.length; i++) { html += `<div style="background-color: ${data[i].c}">${data[i].id}</div>`; } container.innerHTML = html; })(); // 函数 获取随机颜色 function getColor() { const r = () => parseInt(Math.random() * 256); return `rgb(${r()},${r()},${r()})`; } // 按钮点击事件 btnAdd.addEventListener("click", () => { // 获取最新的 div 节点集合 divs = document.querySelectorAll(".container div"); // 旧的位置 const oldRect = getRect(divs); // 添加元素 const div = document.createElement("div"); div.innerText = data.length + 1; div.style.background = getColor(); container.insertBefore(div, divs[0]); // 获取渲染之后的位置 const newRect = getRect(divs); // 核心原理: 假如有a, b两个节点, c 和 d 两个位置 // 开始页面只有一个 a 节点, a 节点在 c 位置 // 插入一个 b 节点在 a 前面之后, a 节点的位置 从 c 到了 d // 我们知道 a 节点在插入前的 位置 c 和 插入后的位置 d // 那么渲染的时候, 新插入节点 b 固定在 a 位置 // a 节点 会出现在 d 位置, 然后通过 css3 转换, 移动到 c 位置 (最开始) // 然后 a 节点 从 c 缓慢移动到 d <== 动画 // 就造成了的 b 把 a 挤过去 的假象 for (let i = 0; i < divs.length; i++) { const left = -(newRect[i].left - oldRect[i].left); const top = -(newRect[i].top - oldRect[i].top); const animate = [ { transform: `translate(${left}px, ${top}px)`, }, { transform: "translate(0, 0)", }, ]; divs[i].animate(animate, 300); } }); // 函数 获取元素的具体方位 function getRect(doms) { const domArea = []; for (let i = 0; i < doms.length; i++) { const rect = doms[i].getBoundingClientRect(); domArea.push({ top: rect.top, left: rect.left, }); } return domArea; } </script> </body> </html>