前端恶趣味:我吸了juejin首页,好爽!


 
今天带来一篇和工作无关的文章。常规的前端业务,其实挺繁琐的,但其实我们可以做一些有意思的事情。当年百度首页的机器人动画,把整个页面内容打碎的特效,也是惊艳了一众前端啊。文本的作者,也是做了一个有意思的事情,把页面内容以动画的形式,逐个吸收掉,看动画有点像孙猴子的金箍棒在收妖精 感兴趣的同学可以看看,自己能不能有机会用上

下面是正文部分。


有位古人说过,人不是在烦的路上,就是正在烦!

852740758443a0028ca93a830e2b7b5f.jpeg

最近在逛某乎,总是会刷到很多让人焦虑的话题!比如这样:

af90cb67c159b1c39a06382aef2e172a.jpeg

或者这样:

732847f1d46c24beb1c2130cd95921b8.jpeg

还有这样:

2b75bbbd585e15f8ef2f2c10ff7453a3.jpeg

然后,就这么一焦虑,我就不怀好意的把目光转向了juejin,要不要把juejin砸了,让面向搜索编程者没有地儿找,少一些竞争者?

首先引入脑海的是二向箔,作为“吾主”最强大武器之一,我打算用这个收 juejin 首页!

说干就干!建个demo,打个样品:

<!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>Add Span to Each Character</title>
</head>
<body style="background-color: #fff;">
  <!-- Your HTML content here -->
  <p>This is some text.</p>
  <div>
    <p>More text here.</p>
    <image src="./3333.png" />
    <video></video>
  </div>
  <img src="./3333.png" />
  <style>
    .aaa {
      height: 100px;
    }
  </style>
</body>
</html>

再来一段JS,用来模拟一根棍进入效果:

<script>
    'use strict';
    const targetNodeObject = document.body;
    const styleElement = document.createElement('style');
    const floatBall = document.createElement('div');
    let center = { centerX: 0, centerY: 0 };
    const timer = 32;
    styleElement.textContent = `
          .highlighted-text {
            width: 4px;
            height: 4px;
            margin: 0 2px;
            display: inline-block;
            overflow: hidden;
            background-color: #f00;
            transition: all 1s;
          }
          .highlighted-image {
            margin: 0 2px !important;
            display: inline-block !important;
            overflow: hidden !important;
            background-color: #000 !important;
            transition: all 1s;
            transform-origin: center top;
          }
          .floating-ball {
            width: 4px;
            position:fixed;
            height: 100px;
            background: #000 !important;
            animation: floatAnimation 0.2s linear infinite;
            left: 50%;
            top: -800px;
            transition: top 1s ease;
            z-index:9999;
            border:1px solid #fff;
            transform-origin: center center;
          }
          @keyframes floatAnimation {
            to {
              transform: rotate(360deg);
            }
          }
          .inhaleAnimation{
            position: fixed;
            z-index:9999;
            animation: inhaleAnimation 2s ease-out forwards;
          }
          @keyframes inhaleAnimation {
            to {
              transform: scale(0);
            }
          }
        `;
    document.head.appendChild(styleElement);

    createAttack();
    
    // 创建进攻武器 - 棍
    function createAttack(callback) {
      floatBall.classList.add('floating-ball');
      targetNodeObject.appendChild(floatBall);
      setTimeout(() => {
        floatBall.style.top = "100px";
        setTimeout(() => {
          if (callback) callback();
        }, 1000)
      }, 100)
    }
</script>

大致效果是这样:

579f3d4e91a5ff143ae2818cfcd1dc37.gif

好了,第一步完成,接下去想攻击效果,基本思路就是模拟二向箔。二向箔是三维物体二维化,那我们juejin首页是二维物体,那我就一维化。效果就是文字化成点。图片变成线。

完整代码如下:

<script>
    'use strict';
    const targetNodeObject = document.body;
    const styleElement = document.createElement('style');
    const floatBall = document.createElement('div');
    let center = { centerX: 0, centerY: 0 };
    const timer = 32;
    styleElement.textContent = `
          .highlighted-text {
            width: 4px;
            height: 4px;
            margin: 0 2px;
            display: inline-block;
            overflow: hidden;
            background-color: #f00;
            transition: all 1s;
          }
          .highlighted-image {
            margin: 0 2px !important;
            display: inline-block !important;
            overflow: hidden !important;
            background-color: #000 !important;
            transition: all 1s;
            transform-origin: center top;
          }
          .floating-ball {
            width: 4px;
            position:fixed;
            height: 100px;
            background: #000 !important;
            animation: floatAnimation 0.2s linear infinite;
            left: 50%;
            top: -800px;
            transition: top 1s ease;
            z-index:9999;
            border:1px solid #fff;
            transform-origin: center center;
          }
          @keyframes floatAnimation {
            to {
              transform: rotate(360deg);
            }
          }
          .inhaleAnimation{
            position: fixed;
            z-index:9999;
            animation: inhaleAnimation 2s ease-out forwards;
          }
          @keyframes inhaleAnimation {
            to {
              transform: scale(0);
            }
          }
        `;
    document.head.appendChild(styleElement);



    function createBall(callback) {
      floatBall.classList.add('floating-ball');
      targetNodeObject.appendChild(floatBall);
      setTimeout(() => {
        floatBall.style.top = "100px";
        setTimeout(() => {
          if (callback) callback();
        }, 1000)
      }, 100)
    }
    createBall(() => {
      init();
      center = getElementCenter(floatBall);
    });
    // 获取棍的中心点位置
    function getElementCenter(element) {
      const rect = element.getBoundingClientRect();
      const centerX = rect.left + rect.width / 2;
      const centerY = rect.top + rect.height / 2;

      return { centerX, centerY }
    }
    
    function init() {
      const steps = [];
      const elements = [];

    // 获取当前信息
      function getNodes(element) {
        const textNodes = [];
        const imageNodes = [];

        function traverse(node) {
          hasBackgroundColor(node)
          if (node.nodeType === Node.TEXT_NODE && node.nodeValue.trim() !== '') {
            textNodes.push(node);
          } else if (isImageNode(node)) {
            imageNodes.push(node);
          } else if (node.nodeType === Node.ELEMENT_NODE && !isExcludedNode(node)) {
            for (const childNode of node.childNodes) {
              traverse(childNode);
            }
          }
        }

        function isExcludedNode(node) {
          const excludedTags = ['SCRIPT', 'STYLE', 'LINK', 'IFRAME', 'path'];
          return excludedTags.includes(node.tagName);
        }

// 校验元素是否有背景颜色
        function hasBackgroundColor(node) {
          try {
            const computedStyle = window.getComputedStyle(node);
            const backgroundColor = computedStyle.backgroundColor;
            if (backgroundColor !== 'rgba(0, 0, 0, 0)' && backgroundColor !== 'transparent') {
              node.style.backgroundColor = 'rgba(255, 255, 255, 1)'
            }
          } catch (e) {
          }
        }

        traverse(element);
        return { textNodes, imageNodes };
      }

        // 添加步骤到执行队列
      function addStep(func, node) {
        steps.push(func.bind(this));
        elements.push(node);
      }
      // 开始步骤
      function startStep() {
        const interTimer = setInterval(() => {
          if (steps.length > 0) {
            const func = steps.shift();
            const element = elements.shift();
            if (func) {
              func();
              if (element) {
                const rect = element.getBoundingClientRect();
                element.style.top = `${rect.top}px`;
                element.style.left = `${rect.left}px`;
                addStep(() => {
                  element.classList.add('inhaleAnimation');
                  element.style.top = `${center.centerY}px`;
                  element.style.left = `${center.centerX}px`;
                  element.addEventListener('animationend', function () {
                    element.remove();
                  });
                })
              }
            }
          } else {
            clearInterval(interTimer);
          }
        }, timer)
      }
      
      const { textNodes, imageNodes } = getNodes(targetNodeObject);
      // 处理文字
      function textFn(node) {
        const textContent = node.nodeValue.trim();
        const newSpan = document.createElement('span');
        const newContent = Array.from(textContent).map(char => {
          const charSpan = document.createElement('span');
          charSpan.textContent = char;
          addStep(() => {
            charSpan.classList.add("highlighted-text");
          }, charSpan);
          newSpan.appendChild(charSpan);
          return charSpan.outerHTML;
        }).join('');
        node.parentNode.replaceChild(newSpan, node);
      };
      // 处理图片
      function imageFn(node) {
        const charSpan = document.createElement('span');
        node.classList.add("highlighted-image");
        addStep(() => {
          const rect = node.getBoundingClientRect();
          charSpan.style.width = `${node.width || node.clientWidth || rect.width}px`;
          charSpan.style.height = `${node.height || node.clientHeight || rect.width}px`;
          charSpan.classList.add("highlighted-image");
          node.replaceWith(charSpan);
          addStep(() => {
            charSpan.style.width = "4px";
            //charSpan.style.transform = `scale(1) rotate(${30}deg)`
          }, charSpan);
        });
      }
      textNodes.forEach(node => {
        if (node.nodeValue.indexOf('@font-face') !== -1) { return; }
        textFn(node);
      });
      imageNodes.forEach(node => {
        imageFn(node);
      });
      
    // 判断是否为图片
    function isImageNode(node) {
      if (node.tagName === 'BODY') { return; }
      const imageTags = ['IMG', 'IMAGE', 'VIDEO', 'svg', "FORM", "svg"];
      return imageTags.includes(node.tagName) &&
        !(node.tagName === 'BODY' && node.classList.contains('floating'));
    }

      startStep();
    }
  </script>

效果为:

效果好像还不错!

现在,将上面JS代码压缩

通过在url输入框内,输入Javascrip:【上面压缩的代码】

将代码放入juejin某页,一起look下效果(截取一段):

看到这里的朋友也知道,这个也就一个前端佬的自嗨了。

但还是想说....

fd9fe2bd0cd1bd292005a21b1cb5008e.jpeg

原文链接:https://juejin.cn/post/7416664040401600539

作者:大怪

最后:

React Hook 深入浅出

CSS技巧与案例详解

vue2与vue3技巧合集

VueUse源码解读

要实现 HTML 中的输入框(`<input>`)宽度自适应内容,可以采用以下几种方法。每种方法都有其适用场景,具体选择取决于实际需求。 ### 1. 使用 `size` 属性 HTML 的 `<input>` 元素支持 `size` 属性,它表示输入框中可以显示的字符数。根据字符数,输入框的宽度会相应变化。 ```html <input type="text" size="10" /> ``` 此方法适合内容长度相对固定的场景。但无法动态响应内容变化,因此在需要动态调整宽度时并不适用[^1]。 --- ### 2. 使用 `width: fit-content` CSS 提供了 `width: fit-content` 属性,可以让元素的宽度自动适应其内容。 ```html <input type="text" style="width: fit-content;" /> ``` 此方法简单且有效,但需要注意浏览器兼容性问题。现代浏览器普遍支持此属性,但在一些旧版浏览器中可能无法正常工作[^2]。 --- ### 3. 使用 JavaScript 动态调整宽度 如果输入框的内容会动态变化,并需要实时调整宽度,则可以使用 JavaScript 动态计算并设置宽度。 ```html <input type="text" id="autoWidthInput" /> ``` ```javascript const input = document.getElementById('autoWidthInput'); // 创建一个隐藏的 span 元素,用于测量文本宽度 const span = document.createElement('span'); span.style.visibility = 'hidden'; span.style.whiteSpace = 'pre'; document.body.appendChild(span); input.addEventListener('input', () => { span.textContent = input.value; input.style.width = span.offsetWidth + 'px'; }); ``` 此方法适用于内容频繁变化的场景,能够确保输入框始终与内容宽度一致。同时,它提供了更高的灵活性和兼容性[^3]。 --- ### 4. 使用 `contenteditable` 的替代方案 如果对 `<input>` 元素的限制较多,可以考虑使用 `<div contenteditable="true">` 替代方案。该方法允许元素内容可编辑,并且可以更灵活地控制样式。 ```html <div contenteditable="true" style="display: inline-block; border: 1px solid #ccc; padding: 5px;"></div> ``` 此方法适用于需要更复杂编辑功能的场景,但需要注意其与表单提交和数据处理的兼容性[^4]。 --- ### 总结 - **静态内容**:使用 `size` 或 `width: fit-content`。 - **动态内容**:使用 JavaScript 动态调整宽度。 - **高级编辑需求**:使用 `contenteditable`。 每种方法各有优劣,应根据实际需求进行选择。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值