6、轮播图之rem|原生|jQuery、表格(合并嵌套固定栏滑动栏滚动条)、js画曲线、验证码 、css汇总(光标滚动条兄弟叠加、tabIndex和a标签、清除浮动悬停改样式、媒体查询BEM栅格布局图片撑开盒子、变量密码禁用显式隐式点击事件、旋转正方体太极图半包围、单词破裹空白省略号、换行回车空格分页、符号图标龙棋伪类伪元素三角形箭头、各种灰色)、原生水印(3150行)

一、rem版轮播图
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>rem版轮播图</title>
      <style>
        *{
          margin:0;
          padding:0;
        }
        html{
          font-size: 20px;
        }
        @keyframes aaa {
          from {left:0;}
          to {left:-400rem;}
        }
        #aaa{
          margin:0 auto;
          width:80rem;
          height:40rem;
          overflow: hidden;
          position: relative;
          background: red;
        }
        #bbb{
          position: absolute;
          width:400rem;
          height:40rem;
        }
        #bbb div{
          width:80rem;
          height:40rem;
          float:left;
          background-size: cover;  /* 确保背景图覆盖整个div */
          background-position: center;  /* 背景图居中显示 */
        }
        #bbb:hover{
          animation: aaa 15s ease-in 0.5s infinite;
        }
      </style>
    </head>
    <body>
      <div id="aaa">
        <div id="bbb">
          <div style="background-image: url('http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517838828_mthumb.jpg');"></div>
          <div style="background-image: url('http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517841171_mthumb.jpg');"></div>
          <div style="background-image: url('http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517843343_mthumb.jpg');"></div>
          <div style="background-image: url('http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517845828_mthumb.jpg');"></div>
          <div style="background-image: url('http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517848093_mthumb.jpg');"></div>
          <div style="background-image: url('http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517850750_mthumb.jpg');"></div>
        </div>
      </div>
      <div style="text-align: center;font-size: 4rem;">将鼠标置于图片之上,图片就开始轮播</div>
    </body>
  </html>

二、原生版轮播图(面对对象)
  <!DOCTYPE html>
  <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>轮播图组件</title>
      <style>
        * {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
        }
        body {
          font-family: Arial, sans-serif;
          padding: 20px;
          background-color: #f5f5f5;
        }
        #boxQC {
          border: 1px solid #ddd;
          border-radius: 8px;
          box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
          background: white;
        }
        .banner-item {
          display: flex;
          justify-content: center;
          align-items: center;
        }
        .banner-controls {
          transition: opacity 0.3s;
        }
        .banner-pagination li {
          transition: background 0.3s;
        }
        .banner-pagination li.on {
          background: #ff4757 !important;
          transform: scale(1.2);
        }
      </style>
    </head>
    <body>
      <div id="boxQC">
        <div class="banner-container">
          <div class="banner-wrapper">
            <div class="banner-item">
              <img src="http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517838828_mthumb.jpg" alt="风景图片1" />
            </div>
            <div class="banner-item">
              <img src="http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517841171_mthumb.jpg" alt="风景图片2" />
            </div>
            <div class="banner-item">
              <img src="http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517843343_mthumb.jpg" alt="风景图片3" />
            </div>
            <div class="banner-item">
              <img src="http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517845828_mthumb.jpg" alt="风景图片4" />
            </div>
            <div class="banner-item">
              <img src="http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517848093_mthumb.jpg" alt="风景图片5" />
            </div>
            <div class="banner-item">
              <img src="http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517850750_mthumb.jpg" alt="风景图片6" />
            </div>
            <div class="banner-item">
              <img src="http://img.pconline.com.cn/images/upload/upc/tx/photoblog/1509/06/c5/12226915_12226915_1441517853171_mthumb.jpg" alt="风景图片7" />
            </div>
          </div>
        </div>
      </div>
    </body>
  </html>
  <script>
    class Banner {
      constructor(width, height) {
        this.width = width;
        this.height = height;
        this.step = 0;
        this.timer = null;
        this.isAnimating = false;
        this.initElements();
        this.initStyles();
        this.initEvents();
        this.startAutoPlay();
      }
      initElements() {
        this.oBox = document.getElementById("boxQC");
        this.oBoxInner = this.oBox.querySelector('.banner-wrapper');
        this.aDiv = this.oBoxInner.querySelectorAll('.banner-item');
        // 克隆第一个元素并添加到末尾以实现无缝滚动
        this.oBoxInner.appendChild(this.aDiv[0].cloneNode(true));
        this.aDiv = this.oBoxInner.querySelectorAll('.banner-item');
        // 创建分页器
        this.createPagination();
        // 创建导航按钮
        this.createNavigation();
      }
      initStyles() {
        // 设置容器样式
        this.oBox.style.width = this.width;
        this.oBox.style.height = this.height;
        this.oBox.style.margin = "0 auto";
        this.oBox.style.overflow = "hidden";
        this.oBox.style.position = "relative";
        // 设置轮播区域样式
        this.oBoxInner.style.height = this.height;
        this.oBoxInner.style.position = "absolute";
        this.oBoxInner.style.left = "0";
        this.oBoxInner.style.width = `${parseFloat(this.width) * this.aDiv.length}px`;
        // 设置每个轮播项样式
        this.aDiv.forEach(div => {
          div.style.width = this.width;
          div.style.height = this.height;
          div.style.float = "left";
          const img = div.querySelector('img');
          img.style.width = "100%";
          img.style.height = "100%";
          img.style.objectFit = "cover";
        });
      }
      createPagination() {
        const oUl = document.createElement('ul');
        oUl.className = 'banner-pagination';
        for (let i = 0; i < this.aDiv.length - 1; i++) {
          const li = document.createElement('li');
          li.dataset.index = i;
          oUl.appendChild(li);
        }
        this.oBox.appendChild(oUl);
        this.oUl = oUl;
        this.aLi = this.oUl.querySelectorAll('li');
        // 设置分页器样式
        this.oUl.style.position = "absolute";
        this.oUl.style.right = "10px";
        this.oUl.style.bottom = "10px";
        this.oUl.style.display = "flex";
        this.oUl.style.gap = "10px";
        this.aLi.forEach(li => {
          li.style.width = "18px";
          li.style.height = "18px";
          li.style.listStyle = "none";
          li.style.background = "green";
          li.style.borderRadius = "50%";
          li.style.cursor = "pointer";
          li.style.transition = "all 0.3s";
          li.addEventListener('click', () => {
            if (this.isAnimating) return;
            this.step = parseInt(li.dataset.index);
            this.animate(-this.step * parseFloat(this.width));
            this.updatePagination();
          });
        });
        this.updatePagination();
      }
      createNavigation() {
        // 创建左箭头
        this.oBtnL = document.createElement('button');
        this.oBtnL.className = 'banner-controls banner-prev';
        this.oBtnL.innerHTML = '❮';
        this.oBtnL.setAttribute('aria-label', '上一张');
        // 创建右箭头
        this.oBtnR = document.createElement('button');
        this.oBtnR.className = 'banner-controls banner-next';
        this.oBtnR.innerHTML = '❯';
        this.oBtnR.setAttribute('aria-label', '下一张');
        this.oBox.appendChild(this.oBtnL);
        this.oBox.appendChild(this.oBtnR);
        // 设置导航按钮样式
        const btnStyle = {
          width: "40px",
          height: "40px",
          position: "absolute",
          top: "50%",
          transform: "translateY(-50%)",
          background: "rgba(0,0,0,0.5)",
          color: "white",
          border: "none",
          borderRadius: "50%",
          fontSize: "20px",
          cursor: "pointer",
          opacity: "0",
          transition: "opacity 0.3s",
          display: "flex",
          justifyContent: "center",
          alignItems: "center"
        };
        Object.assign(this.oBtnL.style, btnStyle, {
          left: "10px"
        });
        Object.assign(this.oBtnR.style, btnStyle, {
          right: "10px"
        });
        // 添加按钮事件
        this.oBtnL.addEventListener('click', () => this.prev());
        this.oBtnR.addEventListener('click', () => this.next());
      }
      initEvents() {
        // 鼠标移入移出事件
        this.oBox.addEventListener('mouseenter', () => {
          clearInterval(this.timer);
          this.oBtnL.style.opacity = "1";
          this.oBtnR.style.opacity = "1";
        });
        this.oBox.addEventListener('mouseleave', () => {
          this.startAutoPlay();
          this.oBtnL.style.opacity = "0";
          this.oBtnR.style.opacity = "0";
        });
        // 触摸事件支持
        let startX, endX;
        this.oBox.addEventListener('touchstart', e => {
          startX = e.touches[0].clientX;
          clearInterval(this.timer);
        });
        this.oBox.addEventListener('touchend', e => {
          endX = e.changedTouches[0].clientX;
          if (startX - endX > 50) {
            this.next(); // 向左滑动,下一张
          } else if (endX - startX > 50) {
            this.prev(); // 向右滑动,上一张
          }
          this.startAutoPlay();
        });
      }
      startAutoPlay() {
        clearInterval(this.timer);
        this.timer = setInterval(() => {
          this.next();
        }, 3000);
      }
      next() {
        if (this.isAnimating) return;
        if (this.step >= this.aDiv.length - 1) {
          this.step = 0;
          this.oBoxInner.style.left = "0";
        }
        this.step++;
        this.animate(-this.step * parseFloat(this.width));
        this.updatePagination();
      }
      prev() {
        if (this.isAnimating) return;
        if (this.step <= 0) {
          this.step = this.aDiv.length - 1;
          this.oBoxInner.style.left = -(this.aDiv.length - 1) * parseFloat(this.width) + "px";
        }
        this.step--;
        this.animate(-this.step * parseFloat(this.width));
        this.updatePagination();
      }
      animate(target, duration = 500) {
        this.isAnimating = true;
        const start = parseFloat(this.oBoxInner.style.left) || 0;
        const distance = target - start;
        const startTime = performance.now();
        const animateFrame = (currentTime) => {
          const elapsed = currentTime - startTime;
          const progress = Math.min(elapsed / duration, 1);
          // 使用ease-out缓动函数
          const easing = 1 - Math.pow(1 - progress, 3);
          const currentPos = start + distance * easing;
          this.oBoxInner.style.left = currentPos + "px";
          if (progress < 1) {
            requestAnimationFrame(animateFrame);
          } else {
            this.isAnimating = false;
            // 如果到达克隆的第一张图片,立即跳回实际的第一张
            if (this.step === this.aDiv.length - 1) {
              setTimeout(() => {
                this.oBoxInner.style.transition = 'none';
                this.oBoxInner.style.left = "0";
                this.step = 0;
                setTimeout(() => {
                  this.oBoxInner.style.transition = '';
                }, 50);
              }, 50);
            }
          }
        };
        requestAnimationFrame(animateFrame);
      }
      updatePagination() {
        const activeIndex = this.step % (this.aDiv.length - 1);
        this.aLi.forEach((li, i) => {
          if (i === activeIndex) {
            li.style.background = "#ff4757";
            li.classList.add('on');
          } else {
            li.style.background = "green";
            li.classList.remove('on');
          }
        });
      }
    }
    // 页面加载完成后初始化轮播图
    document.addEventListener('DOMContentLoaded', () => {
      new Banner('1000px', '500px');
    });      
  </script>
    
三、jQuery版轮播图
1、静态方法扩展
  $.myPlugin = { banner: function (width, height) {----插件代码---- } };
  $.myPlugin.banner('500px', '250px');
2、为jQuery实例扩展方法
  $.fn.banner = function(width, height) {
    //插件代码,这里的this指向jQuery选择器选中的元素集合
    return this.each(function() {
      //对每个元素进行操作
    });
  };
  // 调用方式
  $('#banner').banner('500px', '250px');
3、示例
  <!DOCTYPE html>
  <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <title>jQuery 轮播图插件</title>
      <style>
        /* 基础样式重置 */
        * {
          margin: 0;
          padding: 0;
          box-sizing: border-box;
        }
        body {
          padding: 50px;
          background-color: #f5f5f5;
        }
        /* 轮播容器基础样式(插件会动态覆盖宽高) */
        .carousel-container {
          margin: 0 auto;
          overflow: hidden;
          position: relative;
        }
        /* 轮播内容区(绝对定位实现滑动) */
        .carousel-content {
          display: flex;
          /* 子元素横向排列 */
          position: absolute;
          top: 0;
          left: 0;
          transition: left 0.5s ease;
          /* 滑动过渡动画 */
        }
        /* 轮播项样式 */
        .carousel-item {
          flex: 0 0 auto;
          /* 禁止收缩/拉伸,保持固定宽高 */
        }
        .carousel-item img {
          width: 100%;
          height: 100%;
          object-fit: cover;
          /* 图片自适应容器,避免变形 */
        }
        /* 左右箭头按钮 */
        .carousel-btn {
          position: absolute;
          top: 50%;
          transform: translateY(-50%);
          width: 40px;
          height: 40px;
          background-color: rgba(0, 0, 0, 0.5);
          color: #fff;
          border: none;
          border-radius: 50%;
          cursor: pointer;
          font-size: 18px;
          z-index: 10;
          /* 确保按钮在图片上方 */
          display: none;
          /* 默认隐藏,鼠标悬停时显示 */
        }
        .carousel-btn.prev {
          left: 20px;
        }
        .carousel-btn.next {
          right: 20px;
        }
        .carousel-btn:hover {
          background-color: rgba(0, 0, 0, 0.8);
        }
        /* 焦点导航区 */
        .carousel-dots {
          position: absolute;
          bottom: 20px;
          left: 50%;
          transform: translateX(-50%);
          display: flex;
          gap: 8px;
          /* 焦点之间的间距 */
          z-index: 10;
        }
        .carousel-dot {
          width: 12px;
          height: 12px;
          border-radius: 50%;
          background-color: rgba(255, 255, 255, 0.5);
          cursor: pointer;
          transition: background-color 0.3s ease;
        }
        .carousel-dot.active {
          background-color: #fff;
          /* 激活状态的焦点样式 */
        } 
      </style>
      <!-- 引入 jQuery 库(使用 CDN) -->
      <script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.4/jquery.min.js"></script>
    </head>
    <body>
      <!-- 轮播图容器(需设置 class 供插件识别) -->
      <div class="carousel-container" id="myCarousel">
        <!-- 轮播内容区 -->
        <div class="carousel-content">
          <!-- 轮播项(可根据需求增减) -->
          <div class="carousel-item">
            <img src="https://picsum.photos/id/1/800/400" alt="轮播图1">
          </div>
          <div class="carousel-item">
            <img src="https://picsum.photos/id/2/800/400" alt="轮播图2">
          </div>
          <div class="carousel-item">
            <img src="https://picsum.photos/id/3/800/400" alt="轮播图3">
          </div>
          <div class="carousel-item">
            <img src="https://picsum.photos/id/4/800/400" alt="轮播图4">
          </div>
        </div>
        <!-- 左右箭头(插件动态生成,也可手动写死) -->
        <!-- 焦点导航(插件动态生成) -->
      </div>
    </body>
  </html>
  <script>
    (function($) {
      $.fn.carousel = function(options) {
        const defaultSettings = {
          width: '800px', // 轮播图宽度
          height: '400px', // 轮播图高度
          autoPlay: true, // 是否自动轮播
          interval: 3000, // 自动轮播间隔(毫秒)
          showDots: true, // 是否显示焦点导航
          showBtns: true // 是否显示左右箭头
        };
        const settings = $.extend({}, defaultSettings, options);
        return this.each(function() {
          const $container = $(this); // 轮播容器
          const $content = $container.find('.carousel-content'); // 轮播内容区
          const $items = $container.find('.carousel-item'); // 轮播项
          const itemCount = $items.length; // 轮播项数量
          let currentIndex = 0; // 当前显示的索引
          let autoPlayTimer = null; // 自动轮播定时器
          const itemWidth = parseFloat(settings.width); // 单个轮播项宽度(与容器宽度一致)
          function init() {
            setBaseStyle();
            if (settings.showDots) {
              createDots();
            }
            if (settings.showBtns) {
              createBtns();
            }
            if (settings.autoPlay) {
              startAutoPlay();
            }
            bindHoverEvent();
          }
          function setBaseStyle() {
            $container.css({
              width: settings.width,
              height: settings.height
            });
            $content.css({
              width: itemWidth * itemCount + 'px',
              height: settings.height
            });
            $items.css({
              width: settings.width,
              height: settings.height
            });
          }
          function createDots() {
            const $dotsContainer = $('<div class="carousel-dots"></div>');
            for (let i = 0; i < itemCount; i++) {
              const $dot = $('<span class="carousel-dot"></span>');
              if (i === currentIndex) {
                $dot.addClass('active');
              }
              $dot.on('click', function() {
                currentIndex = i;
                slideTo(currentIndex); 
              });
              $dotsContainer.append($dot);
            }
            $container.append($dotsContainer);
          }
          function createBtns() {
            const $prevBtn = $('<button class="carousel-btn prev"><</button>');
            $prevBtn.on('click', function() {
              currentIndex = (currentIndex - 1 + itemCount) % itemCount;
              slideTo(currentIndex);
            });
            const $nextBtn = $('<button class="carousel-btn next">></button>');
            $nextBtn.on('click', function() {
              currentIndex = (currentIndex + 1) % itemCount;
              slideTo(currentIndex);
            });
            $container.append($prevBtn)
              .append($nextBtn);
          }
          function slideTo(index) {
            const slideLeft = -index * itemWidth;
            $content.css('left', slideLeft + 'px');
            if (settings.showDots) {
              $container.find('.carousel-dot')
                .removeClass('active')
                .eq(index)
                .addClass('active');
            }
            currentIndex = index;
          }
          function startAutoPlay() {
            clearInterval(autoPlayTimer);
            autoPlayTimer = setInterval(function() {
              currentIndex = (currentIndex + 1) % itemCount;
              slideTo(currentIndex);
            }, settings.interval);
          }
          function bindHoverEvent() {
            $container.on('mouseenter', function() {
              clearInterval(autoPlayTimer);
              if (settings.showBtns) {
                $container.find('.carousel-btn')
                  .show();
              }
            });
            $container.on('mouseleave', function() {
              if (settings.autoPlay) {
                startAutoPlay();
              }
              if (settings.showBtns) {
                $container.find('.carousel-btn')
                  .hide();
              }
            });
          }
          init();
        });
      };
    })(jQuery);
    $(document)
      .ready(function() {
        $('#myCarousel')
          .carousel({
            // 可根据需求覆盖默认配置,例如:
            // width: '1000px',
            // height: '500px',
            // interval: 2000,
            // showDots: true,
            // showBtns: true
          });
      });
  </script>

四、table表格
1、合并
  <html>
    <head>
      <style type="text/css">
        table{
          border-collapse: collapse;/* 单元格边框与边框之间_合并;separate;单元格边框与边框之间_分开 */
          border-spacing: 5px;/*  <table cellspacing="5"></table> 相邻单元格之间的距离 */
        }
        table tr th,table tr td{
          padding: 5px;/* <table cellpadding="5"></table> 单元格的内容与边框之间的距离 */
          border: 1px solid grey;/* 每个单元格框线_宽度 */
        }
      </style>
    </head>
    <body>
      <table>
        <thead>
          <tr>
            <th>栏1</th>
            <th>栏2</th>
            <th>栏3</th>
            <th>栏4</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td rowspan="2">第1行第1格,跨2行</td>
            <td>第1行第2格</td>
            <td>第1行第3格</td>
            <td>第1行第4格</td>
          </tr>
          <tr>
            <td>第2行第1格</td>
            <td>第2行第2格</td>
            <td>第2行第3格</td>
          </tr>
        </tbody>
        <tbody>
          <tr>
            <td colspan="2">第1行第1格,跨2栏</td>
            <td>第1行第2格</td>
            <td>第1行第3格</td>
          </tr>
          <tr>
            <td>第2行第1格</td>
            <td>第2行第2格</td>
            <td>第2行第3格</td>
            <td>第2行第4格</td>
          </tr>
        </tbody>
        <tfoot>
          <tr>
            <td colspan="4">被纵、横合并的单元格(th、td)及其右侧的单元格都会向右平移1格</td>
          </tr>
        </tfoot>
      </table>
    </body>
  </html>
2、嵌套合并
  <table class="table">
    <thead>
      <tr>
        <th>
          <button>序号</button>
        </th>
        <th ng-repeat="li in title track by $index">
          <button>{{li}}</button>
        </th>
      </tr>
    </thead>
    <tbody ng-repeat="li in list__detail track by $index">
      <tr>
        <td>
          <div>
            <img ng-src="{{''}}" alt=""/>
          </div>
        </td>
        <td ng-bind="li.name"></td>
        <td ng-bind="li.name"></td>
        <td ng-bind="li.name"></td>
        <td ng-bind="li.name"></td>
      </tr>
      <tr ng-if="li.is_show">
        <td colspan="9999"><!-- 这点很重要,td下面放div -->
          <div id="JSONData">
            <div>此处可以通过jquery.json-viewer.js,格式化JSON数据</div>
            <div>js代码为$('#JSONData').jsonViewer(JSON.parse(data))</div>
          </div>
        </td>
      </tr>
      <tr ng-if="li.is_show">
        <td colspan="9999"><!-- 这点很重要,td下面放table -->
          <table>
            <thead>
              <tr>
                <th>文字</th>
                <th>文字</th>
                <th>文字</th>
                <th>文字</th>
                <th>文字</th>
              </tr>
            </thead>
            <tbody>
              <tr ng-repeat="item in list track by $index">
                <td ng-bind="item.name"></td>
                <td ng-bind="item.name"></td>
                <td ng-bind="item.name"></td>
                <td ng-bind="item.name"></td>
                <td ng-bind="item.name"></td>
              </tr>
              <tr>
                <td colspan="9999" ng-if="list.length<1">暂无数据可显示。</td>
              </tr>
            </tbody>
          </table>
          <!-- 此处分页组件 -->
        </td>
      </tr>
    </tbody>
  </table>
3、table表格数据,嵌套变平级
  var allDatas = res.data[0];
  var fourMenuLast = [];
  var repeatA = '';
  var repeatB = '';
  angular.forEach(allDatas.children, function (item1) {//步骤1,40
    var fourMenu = {//步骤2,
      myMenu1: {},
      myMenu2: {},
      myMenu3: {}
    };
    fourMenu.myMenu1.name = item1.name;//步骤3,
    fourMenu.myMenu1.id = item1.id;//步骤4,
    fourMenu.myMenu1.showname = item1.name;//步骤5,
    angular.forEach(item1.children, function (item2) {//步骤6,20,
      fourMenu.myMenu2.name = item2.name;//步骤7,21,
      fourMenu.myMenu2.id = item2.id;//步骤8,22,
      fourMenu.myMenu2.showname = item2.name;//步骤9,23,
      angular.forEach(item2.children, function (item3) {//步骤10,15,24,30,35,
        fourMenu.myMenu3.name = item3.name;//步骤11,16,25,31,36,
        fourMenu.myMenu3.id = item3.id;//步骤12,17,26,27,32,37,
        fourMenu.myMenu3.showname = item3.name;//步骤13,18,28,33,38,
        fourMenuLast.push(angular.copy(fourMenu));//步骤14,19,29,34,39,
      });
    });
    //fourMenuLast=[fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu,fourMenu];
    $scope.rowSpan1 = {}; //某个一级导航合并的行数
    angular.forEach(fourMenuLast, function (item) {
      if ($scope.rowSpan1[item.myMenu1.id]) {
        $scope.rowSpan1[item.myMenu1.id] += 1;
      } else {
        $scope.rowSpan1[item.myMenu1.id] = 1;
      }
      if (item.myMenu1.id === repeatA) {
        item.myMenu1.name = '';
      } else {
        repeatA = item.myMenu1.id;
      }
    });
    $scope.rowSpan2 = {}; //某个二级导航合并的行数
    angular.forEach(fourMenuLast, function (item) {
      if ($scope.rowSpan2[item.myMenu2.id]) {
        $scope.rowSpan2[item.myMenu2.id] += 1;
      } else {
        $scope.rowSpan2[item.myMenu2.id] = 1;
      }
      if (item.myMenu2.id === repeatB) {
        item.myMenu2.name = '';
      } else {
        repeatB = item.myMenu2.id;
      }
    });
    $scope.allroles = fourMenuLast;
  })
4、table表格示例(含固定栏和滑动栏)
  <!DOCTYPE html>
  <html lang="en">
  <head>
      <title>左侧固定宽,右侧占满剩余宽</title>
      <meta charset="utf-8"/>
      <script src="https://cdn.bootcss.com/jquery/1.11.1/jquery.js"></script>
      <style>
        * {
          padding: 0;
          margin: 0;
        }
        /* 设置滚动条的样式 */
        ::-webkit-scrollbar {
          width: 17px;      
          height: 17px;
        }
        /* 滚动槽 */
        ::-webkit-scrollbar-track { 
          background: #dabda2;
          border-radius: 10px;
          box-shadow: inset 0 0 6px rgba(0, 0, 0, 0.3);
          border-top-left-radius: 0
        }
        /* 滚动条滑块*/
        ::-webkit-scrollbar-thumb {
          background: #bb8459;
          border-radius: 10px;
          box-shadow: inset 0 0 6px gray;
        }
        table {
          border: 1px solid black;
          width: 1000px;
          color: #000000;
          text-align: center;
          border-collapse: collapse;
        }
        table thead tr th,
        table tbody tr td {
          border: 1px solid black;
          min-width: 100px;
          white-space: nowrap;
          padding: 5px 20px;
        }
        .leftDiv{
          width: 300px;
          padding: 20px;
          border-radius: 10px;
          border:1px solid gray
        }
        .leftDiv p{
          padding:10px;
          font-size: 20px;
        }
      </style>
      <script type="text/javascript">
        $(document).ready(function () {
          //表头数据
          const tableHeaders = [
            "姓名", "语文", "数学", "英语", "政治", "历史", 
            "地理", "物理", "化学", "生物", "体育", "音乐", "美术", "备注"
          ];
          //调用封装的函数创建表格
          createScrollableTable(tableHeaders, 'rightDiv', 300);
          function createScrollableTable(headers, containerId, width) {
            //初始化函数
            function init() {
              createTable(headers, containerId);
              var str = makeTableBodyString();
              width = document.body.clientWidth - width;
              $("tbody").html(str);
              fixColumnTable("myTable", 4, width, 600);
            }
            //窗口调整大小时重新初始化
            window.onresize = function () {
              init();
            }
            //初始化表格
            init();
            //创建表格结构
            function createTable(headers, containerId) {
              var tableHtml = '<table id="myTable">';
              tableHtml += '<thead><tr>';
              for (var i = 0; i < headers.length; i++) {
                tableHtml += '<th>' + headers[i] + '</th>';
              }
              tableHtml += '</tr></thead>';
              tableHtml += '<tbody></tbody>';
              tableHtml += '</table>';
              $('#'+containerId).html(tableHtml);
            }
            //固定列表格函数
            function fixColumnTable(tableID, fixColumnNumber, width, height) {
              //1、以下给"哥哥表格"添加1个"弟弟div"
              var tableIDSibling = "";
              tableIDSibling += "<div id='" + tableID + "sibling' style='overflow:hidden;height:" + height + "px;";
              tableIDSibling += " width:" + width + "px;border:1px solid gray;border-radius:10px;'></div>";
              $("#" + tableID).after(tableIDSibling);
              //2、以下在"弟弟div"内,插入4个"儿子div"
              var tableIDSiblingInner = "";
              tableIDSiblingInner += '<div id="' + tableID + 'HeadLeft"></div>';
              tableIDSiblingInner += '<div id="' + tableID + 'HeadRight"></div>';
              tableIDSiblingInner += '<div id="' + tableID + 'BodyLeft"></div>';
              tableIDSiblingInner += '<div id="' + tableID + 'BodyRight"></div>';
              $(tableIDSiblingInner).appendTo("#" + tableID + "sibling");
              //3、以下让头左、头右、体左溢出隐藏,让体右溢出滚动,并设置堆叠顺序
              $("#" + tableID + "HeadLeft").css({ "overflow": "hidden", "z-index": "50", "background-color": "yellow" });
              $("#" + tableID + "HeadRight").css({ "overflow": "hidden", "z-index": "45", "background-color": "red", "width": width - 17 });
              $("#" + tableID + "BodyLeft").css({ "overflow": "hidden", "z-index": "40", "background-color": "green", "height": height - 17 });
              $("#" + tableID + "BodyRight").css({ "overflow": "scroll", "z-index": "35", "width": width, "height": height });
              //4、以下给"哥哥表格"和3个克隆,分别插入到4个"儿子div"中,生成4个"孙子表格"
              var oldTable = $("#" + tableID);
              var HeadLeft = oldTable.clone(true);
              var HeadRight = oldTable.clone(true);
              var BodyLeft = oldTable.clone(true);
              $("#" + tableID + "HeadLeft").append(HeadLeft);
              $("#" + tableID + "HeadRight").append(HeadRight);
              $("#" + tableID + "BodyLeft").append(BodyLeft);
              $("#" + tableID + "BodyRight").append(oldTable);
              //附1、append与appendChild
              //(1)原生的appendChild与jQuery的append,在处理已存在的DOM节点时,都将节点从原位置移动到新位置(而非复制)
              //(2)3个等效,targetContainer.appendChild(oldTable)、$targetContainer.append(oldTable)、$(oldTable).appendTo(targetContainer)
              //5、以下获取"哥哥表格"头部固定栏的高和左侧固定栏的宽
              var columnsWidth = 2;
              var headHeight = $("#" + tableID + " thead").height()+2;
              $("#" + tableID + " tr:last td:lt(" + fixColumnNumber + ")").each(function () {
                columnsWidth += $(this).outerWidth(true);
              });
              //6、以下限制高层级的宽、高,让低层级BodyRight显示出来
              $("#" + tableID + "HeadLeft").css({"width":columnsWidth, "height":headHeight});
              $("#" + tableID + "HeadRight").css("height", headHeight);
              $("#" + tableID + "BodyLeft").css("width", columnsWidth);
              //7、以下将4个"儿子div"移到"弟弟div"的正上方,原位置空出(position: relative)
              $("#" + tableID + "HeadLeft").offset($("#" + tableID + "sibling").offset());//$HeadLeft.css({top: -1, left: -1})
              $("#" + tableID + "HeadRight").offset($("#" + tableID + "sibling").offset());//$HeadRight.css({top: -35, left: -1})
              $("#" + tableID + "BodyLeft").offset($("#" + tableID + "sibling").offset());//$BodyLeft.css({top: -69, left: -1})
              $("#" + tableID + "BodyRight").offset($("#" + tableID + "sibling").offset());//$BodyRight.css({top: -652, left: -1})
              //附2、offset方法:"获取"或"设置"匹配元素相对于文档的偏移
              //(1)"获取"借助于getBoundingClientRect来实现
              //(2)"设置"借助于jQuery.offset.setOffset来实现,其内部用position"获取"父级定位元素的位置
              //(3)在setOffset源码中,相对定位和绝对定位走不同的逻辑,实现相同的定位效果
              //7、以下当第4个"儿子div"的滑动时,第2个或第3个"儿子div"同步滑动
              $("#" + tableID + "BodyRight").scroll(function () {
                $("#" + tableID + "HeadRight").scrollLeft($("#" + tableID + "BodyRight").scrollLeft());
                $("#" + tableID + "BodyLeft").scrollTop($("#" + tableID + "BodyRight").scrollTop());
              });
            }
            // 生成表格内容字符串
            function makeTableBodyString() {
              var firstName = '赵钱孙李周吴郑王冯陈褚卫蒋沈韩杨朱秦尤许何吕施张孔曹严华金魏陶江';
              var lastName = '枯藤老树昏鸦小桥流水人家古道西风瘦马夕阳西下断肠人在天涯马致远天净沙秋思';
              var ary = [];
              for (var i = 1; i <= 60; i++) {
                var obj = {
                  name: firstName[randomMath(0, 31)] + lastName[randomMath(0, 35)] + lastName[randomMath(0, 35)],
                  yuwenScore: randomMath(60, 100) + "分",
                  shuxueScore: randomMath(60, 100) + "分",
                  yingyuScore: randomMath(60, 100) + "分",
                  zhengzhiScore: randomMath(60, 100) + "分",
                  lishiScore: randomMath(60, 100) + "分",
                  diliScore: randomMath(60, 100) + "分",
                  wuliScore: randomMath(60, 100) + "分",
                  huaxueScore: randomMath(60, 100) + "分",
                  shengwuScore: randomMath(60, 100) + "分",
                  tiyuScore: randomMath(60, 100) + "分",
                  yingyueScore: randomMath(60, 100) + "分",
                  meishuScore: randomMath(60, 100) + "分"
                };
                ary.push(obj);
              }
              var str = ""
              $.each(ary, function (index, item) {
                str += "<tr>";
                str += "<td>" + item.name + "</td>";
                str += "<td>" + item.yuwenScore + "</td>";
                str += "<td>" + item.shuxueScore + "</td>";
                str += "<td>" + item.yingyuScore + "</td>";
                str += "<td>" + item.zhengzhiScore + "</td>";
                str += "<td>" + item.lishiScore + "</td>";
                str += "<td>" + item.diliScore + "</td>";
                str += "<td>" + item.wuliScore + "</td>";
                str += "<td>" + item.huaxueScore + "</td>";
                str += "<td>" + item.shengwuScore + "</td>";
                str += "<td>" + item.tiyuScore + "</td>";
                str += "<td>" + item.tiyuScore + "</td>";
                str += "<td>" + item.meishuScore + "</td>";
                str += "<td>" + "===================================" + "</td>";
                str += "</tr>";
              })
              return str;
            }
            //随机数生成函数
            function randomMath(n, m) {
              return Math.round(Math.random() * (m - n) + n);
            }
          }
        });
      </script>
    </head>
    <body>
      <div style="display: flex;">
        <div class="leftDiv">
          <p>效果说明:</p>
          <p>左侧固定宽,</p>
          <p>右侧表格占满余下宽,</p>
          <p>窗口宽度变化时,</p>
          <p>依然保持这个效果。</p> 
        </div>
        <div style="flex:1;height:600px" id="rightDiv">
        </div>
      </div>
      <!-- 模仿来源https://blog.csdn.net/qq_29170981/article/details/79221644 -->
    </body>
  </html>

五、js根据表达式画曲线
1、示例一:
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title></title>
      <script src="https://unpkg.com/d3@3/d3.min.js"></script>
      <script src="https://unpkg.com/function-plot@1/dist/function-plot.js"></script>
    </head>
    <body>
      <div id="box" style="width:500px;height:1000px"></div>
    </body>
  </html>
  <script>
    //参数说明 https://www.javascriptcn.com/read-36947.html
    functionPlot({
      target:document.getElementById("box"),
      data:[
        {fn:"x^4+x^3+x^2+x+1"}
      ],
      title:"ddddddd",
      xAxis:{
        domain:[-5,5],
      },
      yAxis:{
        domain:[0,70],
      }
    })
  </script>
2、示例二:
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <title></title>
    <script src="https://unpkg.com/d3@3/d3.min.js"></script>
    <script src="https://unpkg.com/function-plot@1/dist/function-plot.js"></script>
  </head>
  <body>
  <div id="box" style="width:500px;height:1000px"></div>
  <script>
    functionPlot({
      target:document.getElementById("box"),
      data:[
        {fn:"sqrt(4-x^2)"},
        {fn:"-sqrt(4-x^2)"},
      ],
      title:"ddddddd",
      xAxis:{
        domain:[-5,5],
      },
      yAxis:{
        domain:[-3,3],
      },
      grid:true,
      disableZoom:true  
    })
  </script>
  </body>
  </html>
3、示例三:
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>plot 绘制图像</title>
      <script src="https://cdn.plot.ly/plotly-1.2.0.min.js"></script>
    </head>
    <body>
      <div id="tester" style="width:600px;height:250px;"></div>
    </body>
    <!-- test -->
  </html>
  <script>
    TESTER = document.getElementById('tester');
    Plotly.plot(
      TESTER, 
      [{
        x: [1, 2, 3, 4, 5],
        y: [1, 2, 4, 8, 16]
      }], 
      {
        margin: {t: 0}
      }
    );
  </script>
4、示例四:
  <!DOCTYPE html>
  <html>
  <head>
    <meta charset="UTF-8">
    <title>plot 绘制图像</title>
    <script src="https://cdn.plot.ly/plotly-1.2.0.min.js"></script>
  </head>
  <body>
    <div id="math-function" style="width:600px;height:250px;"></div>
  </body>
  <!-- test -->
  </html>
  <script>
    TESTER = document.getElementById('math-function');
    var x = [], y = [];
    for(var i = -10; i < 10; i += 1) {
      x.push(i);
      y.push(2*i+1);
    }
    Plotly.plot(
      TESTER, 
      [{
        x: x,
        y: y
      }], 
      {
        margin: {t: 0}
      }
    );
  </script>

 
六、10行实现不重复验证码 
  不重复的6位随机验证码非常常用,这里是最简单的两种实现方式:(1)自动生成,(2)点击更新。
1、自动生成:每隔1秒自动生成1次。
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <style>
      #yard{
        font-size: 170px;
        background: grey;
      }
    </style>
    <body>
      <div id="yard"></div>
    </body>
  </html>
  <script>
    var div = document.getElementById("yard");
    function fn(){
      var strCode = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
      var str = '';
      while (str.length < 6) {
        var str1 = strCode.charAt(Math.round(Math.random() * 61));
        if (str.indexOf(str1) === -1) {
          str += str1;
        }
      }
      div.innerHTML=str;
    }
    fn();
    var timer=setInterval(fn,1000)
  </script>
2、点击更新
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
    </head>
    <style>
      #yard{
        font-size: 170px;
        background: grey;
        cursor: pointer;/*比“1、自动生成”多出的代码*/
        -webkit-user-select:none;/*比“1、自动生成”多出的代码*/
      }
    </style>
    <body>
      <div id="yard"></div>
    </body>
  </html>
  <script>
    var div = document.getElementById("yard");
    div.onclick=fn;
    function fn(){
      var strCode = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
      var str = '';
      while (str.length < 6) {
        var str1 = strCode.charAt(Math.round(Math.random() * 61));
        if (str.indexOf(str1) === -1) {
          str += str1;
        }
      }
      div.innerHTML=str;
    }
    fn();
  </script>

七、css汇总
1、光标、选中、空白
 (1)cursor: pointer;光标类型:小手
 (2)user-select: none;用户选择:禁止选中文字
 (3)white-space: nowrap;超宽:不换行
2、改查
 (1)obj.style可读(写在元素标签中,style属性里的样式)、可写(如下)
    A、obj.style.width = '400px';
    B、obj.style.cssText = "width:400px; height:300px";
 (2)getComputedStyle可读(元素的所有样式,如下)、不可写  
    A、var first=document.getElementById("first");
    B、window.getComputedStyle(first,null).backgroundColor
3、滚动条
 (1)破坏布局,
   A、现象,当内容增多,滚动条从无到有时,它的出现挤压了内容宽度,导致原来设计好的布局被破坏
   B、原因,滚动条的宽度是计算到内容 content 里的
   C、解决,增加一个中间层,使得外部容器宽度保持设计宽度,内部元素排列保持不变
 (2)隐藏
    A、*::-webkit-scrollbar {
        display: none;
      }
    B、::-webkit-scrollbar {
        width: 0;
      }
4、兄弟叠加
  (1)需求:
    A、哥哥盒子不隐藏,弟弟盒子放在哥哥盒子上,
    B、哥哥盒子隐藏,弟弟元素占据哥哥盒子的位置,
  (2)实现:
    <div class="up" v-if="!showList"></div>
    <div></div> 
    .up{
      margin-bottom: -90px;
    }
5、tabIndex和a标签
  (1)给div标签添加属性tabIndex=-1,可以使该div有onfocus与onblur事件
  (2)a标签focus()不聚焦解决方案,给它添加属性href为#
  (3)a标签有黑框解决方案,设置样式outline为none
  (4)a标签点击后,响应用户自定义的点击事件
    <a href="javascript:void(0);" onclick="doSomething()">test</a>
    <a href="javascript:;" onclick="doSomething()">test</a>
    <a href="####" onclick="doSomething()">test</a>
6、a标签之target属性的取值
  示例:<a target="_blank|_self|_parent|_top|framename"></a>
  (1)_self:默认,当前页面跳转
  (2)_blank:新窗口打开
  (3)_parent:在父窗口中打开链接
  (4)_top:在当前窗体打开链接,并替换当前的整个窗体(框架页)
7、清除浮动的7种方法
  原因:父元素不浮动,子元素浮动,引发父元素塌陷
  解决方案:
  (1)子级结尾处加空div标签 clear:both
  (2)父级div 也一起浮动
  (3)父级div定义 width
  (4)父级div定义 伪类:after .clearfloat:after{display:block;clear:both;content:"";visibility:hidden;height:0}
  (5)父级div定义 overflow: hidden 或者 auto
  (6)父级div定义 display:table
8、块级元素A及其内部的块级元素B,
  (1)A浮动,B不浮动,B的宽度为B实际需要的宽度,A的宽度由B决定;
  (2)A不浮动,B浮动,会引发A塌陷。
  (3)A和B都浮动,不会引发A塌陷。
9、块级元素A及其弟弟块级元素B的默认宽度是100%;
  (1)A浮动,B不浮动,A的宽度为A实际需要的宽度,B会插到A的下面;
  (2)A不浮动,B浮动,B浮到A的上方。
  (3)A和B都浮动,则A和B同行,宽度为它们实际需要的宽度
10、hover悬停与否,内部显示不同区域
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>样式</title>
      <style>
        .outer .black{
          display: block;
          color: black;
        }
        .outer .gray{
          display: none;
        }
        .outer:hover .black{
          display: none;
        }
        .outer:hover .gray{
          display: block;
          color: gray;
        }
      </style>
    </head>
    <body>
      <div class="outer" >
        <div class="black">正常样式</div>
        <div class="gray">悬停样式</div>
      </div>
    </body>
  </html>
11、媒体查询
  (1)参数实例说明
    @media (width: 900px) {
      body{
        font-size: 50px;
      }
    }
    @media screen and (min-width: 960px) { 
      //all,所有设备;print,打印机等设备;screen,彩色屏幕(手机,电脑,平板)
      body {
        font-size: 50px;
      }
    }
    @media only screen and (min-width: 600px) { //仅适用于屏幕设备
      body {
        background-color: lightblue;
      }
    }
    @media not screen and (max-width: 600px) { //当设备不是屏幕设备
      body {
        background-color: lightgreen;
      }
    }
    @media screen and (min-width: 400px) and (max-width: 800px) and (orientation: landscape) { 
      //portrait,竖屏;landscape,横屏
      body {
        font-size: 200px;
      }
    }
    @media screen and (prefers-reduced-motion:reduce) { //用户在操作系统中启用了减少动画的设置
      .form-control {
        transition: none
      }
    }
  (2)媒体查询的实例
    <!DOCTYPE html>
    <html lang="zh-CN">
      <head>
        <title>媒体查询的实例</title>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
          @media (min-width: 400px) {
            body{
              font-size: 50px;
            }
          }
          @media (min-width: 800px) {
            body{
              font-size: 100px;
            }
          }
          @media (min-width: 1200px) {
            body{
              font-size: 150px;
            }
          }
          @media (min-width: 1600px) {
            body{
              font-size: 200px;
            }
          }
        </style>
      </head>
      <body>
        <div> 最小宽度,只能从小往大写 </div>
      </body>
    </html>
12、BEM
  (1)CSS命名规范,缺点是名称很长
  (2)BEM,块(block)、元素(element)、修饰符(modifier)
  (3)CSS示例
    .overview{} /* 块 */
    .overview__row{} /* 元素 */
    .overview__row--right{} /* 修饰符 */
  (4)HTML示例
    <div class="overview">
      <div class="overview__row overview__row--right"></div>
      <div class="overview__row overview__row--right"></div>
      <div class="overview__row overview__row--right"></div>
    </div> 
13、栅格布局的说明
  (1)col-6,任何网页,占6栏
  (2)col-xs-6,超小网页,占6栏,<768px
  (3)col-sm-6,小网页,占6栏,>=768px
  (4)col-md-6,中等网页,占6栏,>=992px
  (5)col-lg-6,大网页,占6栏,>=1200px
  (6)层级:container-->row-->col-6-->form-inline-->form-group-->form-control
    <body>
      <div class="container">
        <div class="row">
          <div class="col-6">
            <form class="form-inline">
              <div class="form-group">
                <label>todotext</label>
                <input class="form-control" type="text">
              </div>
            </form>
          </div>
        </div>
      </div>
    </body>
  (7)栅格布局的实例
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <title>栅格布局实例</title>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <link rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.3.1/css/bootstrap.min.css">
      <style>
        .border{
          border:1px solid gray
        }
        div{
          font-size: 14px;
        }
      </style>
    </head>
    <body>
      <div class="container">
        <div class="row">
        <div class="border col-xs-4 padding" style="padding:0 15px;">.col-xs-4,"网页的宽"小于768px时,占4格</div>
        </div>  
        <div class="row">
        <div class="border col-sm-4">.col-sm-4,"网页的宽"大于等于768px时,占4格</div>
        <div class="border col-sm-4"></div>
        <div class="border col-sm-4"></div>
        </div>  
        <div class="row">
        <div class="border col-md-4">.col-md-4,"网页的宽"大于等于992px时,占4格</div>
        <div class="border col-md-4"></div>
        <div class="border col-md-4"></div>
        </div>  
        <div class="row">
        <div class="border col-lg-4">.col-lg-4,"网页的宽"大于等于1200px,占4格</div>
        <div class="border col-lg-4"></div>
        <div class="border col-lg-4"></div>
        </div>  
        <div class="row">
        <div class="border col-4">.col-4,不管"网页的宽"是多少,都占4格</div>
        <div class="border col-4"></div>
        <div class="border col-4"></div>
        </div>
      </div>
    </body>
    </html>
14、背景图片撑开盒子
  <!DOCTYPE html>
  <html lang="en">
  <head>
    <meta charset="UTF-8">
    <title>文字随div缩放</title>
    <style>
      * {
        padding: 0;
        margin: 0;
      }
      .outer {
        width: 40%;
        background: url(https://bkimg.cdn.bcebos.com/pic/faf2b2119313b07e1dc9e56303d7912397dd8c61) no-repeat;
        background-size: 100%;
        border: 10px green dashed;
        margin: 0 auto;
        display: flex;
        justify-content: center;
        align-items: center;
        flex-direction: column;
      }
      .outer p {
        font-size: calc(1vw * 2); 
        margin: 0.5em 0;
        /* 
          em的计算规则:
          1、用于font-size属性时,em基于父元素的字体大小
          2、用于其他属性,如margin时,em基于当前元素自身的font-size
        */
      }
    </style>
  </head>
  <body>
    <div class="outer">
      <p>背景图片撑开盒子</p>
      <p>盒子内部可以添加内容</p>
    </div>
  </body>
  </html>
  <script>
    function setDivHeightBasedOnBgImage() {
      const div = document.querySelector('.outer');
      const bgImageUrl = 'https://bkimg.cdn.bcebos.com/pic/faf2b2119313b07e1dc9e56303d7912397dd8c61';
      const img = new Image();
      img.src = bgImageUrl;
      img.onload = function() {
        const aspectRatio = img.height / img.width;
        const divWidth = div.clientWidth;
        div.style.height = `${divWidth * aspectRatio}px`;
      };
      img.onerror = () => console.error('背景图片加载失败');
    }
    window.addEventListener('load', setDivHeightBasedOnBgImage);
    window.addEventListener('resize', setDivHeightBasedOnBgImage);
  </script>
15、变量--var
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>样式变量和禁用密码框的背景色</title>
      <style>
        :root {
          --colorGreen:green;
        }
        html{
          --fontSize40:20px;
        }
        .div{
          color:var(--colorGreen);
          font-size:var(--fontSize40);
        }
        .other{
          --colorGreen:green;
          --fontSize40:20px;
          color:var(--colorGreen);
          font-size:var(--fontSize40);
        }
      </style>
    </head>
    <body>
      <div class="div">
        <pre>
          全局定义样式变量
            :root {
              --colorGreen:green;
            }
            html{
              --fontSize40:40px;
            }
          局部使用样式变量
            .div{
              color:var(--colorGreen);
              font-size:var(--fontSize40);
            }
        </pre>
      </div>
      <div class="other">
        <pre>
          局部定义、使用样式变量
            .other{
              --colorGreen:green;
              --fontSize40:20px;
              color:var(--colorGreen);
              font-size:var(--fontSize40);
            }
        </pre>
      </div>
    </body>
  </html>
16、密码框
  (1)禁用样式
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <title>样式变量和禁用密码框的背景色</title>
        <style>
          input[type=password]:disabled{
            width: 200px;
            height: 30px;
            font-size: 20px;
            border-radius: 6px;
            background: gray;
          }
        </style>
      </head>
      <body>
        <div>
          <pre>
            <label>禁用密码框样式:</label>
            <input disabled type="password" value="这是我的密码"/>
            input[type=password]:disabled{
              
            }
          </pre>
        </div>
      </body>
    </html>
  (2)各种禁止
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>密码框的各种禁止</title>
    </head>
    <body>
      <input
        id="pwd"
        name="pwd"
        type="password"
        autocomplete="off" 
        oncut="return false"
        oncopy="return false"
        onpaste="return false"
        onselectstart="return false"
        oncontextmenu="return false"
        style="width:400px;height:30px;"
      />
      <p>
        <span>禁用自动完成功能:</span>
        <span>autocomplete="off"</span>
      </p>
      <p>
        <span>禁止剪贴:</span>
        <span>oncut = "return false"</span>
      </p>
      <p>
        <span>禁止复制:</span>
        <span>oncopy = "return false"</span>
      </p>
      <p>
        <span>禁止粘贴:</span>
        <span>onpaste = "return false"</span>
      </p>
      <p>
        <span>禁止选中:</span>
        <span>onselectstart="return false"</span>
      </p>
      <p>
        <span>禁止右键:</span>
        <span>oncontextmenu="return false"</span>
      </p>
    </body>
    </html>
17、点击事件之显示执行与隐式执行示例:
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Title</title>
      <style>
        div{width:100px;height:100px;background:red;margin:20px;}
      </style>
    </head>
    <body>
      <div onclick="myClick(event,'second','third')" ></div>
      <div id="myDiv" ></div>
        <!--这是显示执行,有定义,有执行。执行时,除了浏览器向其传参'event',还可以人为传参'second'、'third'-->
    </body>
  </html>
  <script>
    function myClick(first,second,third){
      console.log(first);
      console.log(second);
      console.log(third);
    }
    document.getElementById("myDiv").onclick=function (myEvent) {
    /*这是隐式执行,有定义,无执行。执行时,除了浏览器向其传参'event',没办法人为传参'second'、'third'*/
      console.log(myEvent);
    }
  </script>
18、旋转的正方体
  实现思路:(1)把6个正方形通过定位叠放在一起;(2)1个不动,4个旋转90度与前者垂直,形成1个无盖盒子,最后1个通过平移作为盒盖!(3)添加CSS3动画,实现旋转。
  <!DOCTYPE html>
  <html>
    <head lang="en">
      <meta charset="UTF-8">
      <title>旋转的正方体</title>
      <style type="text/css">
        *{
          margin: 0;
          padding:0
        }
        .wrap{
          perspective:2000px;
        }
        .wrap ul{
          width: 200px;
          height: 200px;
          margin: 100px auto;
          position: relative;
          animation: move 5s infinite;
          transform-style: preserve-3d;
          transform-origin: 100px 100px -100px;
        }
        .wrap ul li{
          left: 0;
          top: 0;
          width: 196px;
          height: 196px;
          color: #fff;
          font-size: 60px;
          list-style: none;
          text-align: center;
          line-height: 196px;
          position: absolute;
          border:2px solid #000;
          background: rgba(000,000,000,0.5);
    
        }
        .wrap ul li:nth-child(1){
          transform-origin: top;
          transform: rotateX(-90deg);
        }
        .wrap ul li:nth-child(2){
          transform-origin: bottom;
          transform: rotateX(90deg);
        }
        .wrap ul li:nth-child(3){
          transform-origin: left;
          transform: rotateY(90deg);
        }
        .wrap ul li:nth-child(4){
          transform-origin: right;
          transform: rotateY(-90deg);
        }
        .wrap ul li:nth-child(5){
          transform: translateZ(-200px);
        }
        .wrap ul li:nth-child(6){
          transform:translateZ(0px);
        }
        @keyframes move {
          0%{
              transform: rotateX(0deg) rotateY(0deg);
          }
          100%{
              transform: rotateX(360deg) rotateY(360deg);
          }
        }
      </style>
    </head>
    <body>
    <div class="wrap">
    <ul>
      <li>1</li>
      <li>2</li>
      <li>3</li>
      <li>4</li>
      <li>5</li>
      <li>6</li>
    </ul>
    </div>
    </body>
  </html>
19、太极图
  (1)三层div实现
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>太极(放大3倍)</title>
        <style class="cp-pen-styles">
          *{
            padding:0;
            margin:0;
          }
          body {
            display: flex;
            justify-content: center;
            align-items: center;
            min-height: 100vh;
          }
          .outer{
            background: linear-gradient(to right, white 50%, black 50%);
            width:600px;  
            height:600px; 
            border-radius: 50%;
            position:relative;
            border:3px solid black; /* 边框也相应放大 */
          }
          .outer .top{
            background: white;
            width:300px;  
            height:300px; 
            border-radius: 50%;
            position:absolute;
            left: 150px;  
            top:0;
          }
          .outer .top .topInner{
            background: black;
            width:120px;  
            height:120px; 
            border-radius: 50%;
            position:absolute;
            left: 90px;  
            top:90px;    
          }
          .outer .bottom{
            background: black;
            width:300px;  
            height:300px; 
            border-radius: 50%;
            position:absolute;
            left: 150px;  
            bottom:0;
          }
          .outer .bottom .bottomInner{
            background: white;
            width:120px;  
            height:120px; 
            border-radius: 50%;
            position:absolute;
            left:90px;   
            top:90px;    
          }
        </style>
      </head>
      <body>
        <div class="outer">
          <div class="top">
            <div class="topInner"></div>
          </div>
          <div class="bottom">
            <div class="bottomInner"></div>
          </div>
        </div>
      </body>
    </html>
  (2)伪类实现
    <!DOCTYPE html>
    <html>
      <head>
        <meta charset="UTF-8">
        <title>标准太极图(放大3倍)</title>
        <style>
          * {
            margin: 0;
            padding: 0;
          }
          body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            background: #f5f5f5;
          }
          #taiji {
            width: 600px;  
            height: 600px; 
            background: linear-gradient(to right, white 50%, black 50%);
            border-radius: 50%;
            border: 3px solid #333; /* 边框放大3倍 */
            position: relative;
            box-shadow: 0 0 30px rgba(0,0,0,0.2); /* 阴影放大 */
          }
          /* 上半部分(白底黑点) */
          #taiji:before {
            content: "";
            position: absolute;
            width: 300px;  
            height: 300px; 
            background: white;
            border-radius: 50%;
            top: 0;
            left: 50%;
            transform: translateX(-50%);
          }
          /* 上半部分的黑点 */
          #taiji:after {
            content: "";
            position: absolute;
            width: 60px;   
            height: 60px;  
            background: black;
            border-radius: 50%;
            top: 150px;    
            left: 50%;
            transform: translateX(-50%);
            z-index: 2;
          }
          /* 下半部分(黑底白点) */
          #taiji .bottom {
            position: absolute;
            width: 300px;  
            height: 300px; 
            background: black;
            border-radius: 50%;
            bottom: 0;
            left: 50%;
            transform: translateX(-50%);
          }
          /* 下半部分的白点 */
          #taiji .bottom:after {
            content: "";
            position: absolute;
            width: 60px;   
            height: 60px;  
            background: white;
            border-radius: 50%;
            top: 150px;    
            left: 50%;
            transform: translateX(-50%);
          }
        </style>
      </head>
      <body>
        <div id="taiji">
          <div class="bottom"></div>
        </div>
      </body>
    </html>
20、换行之半包围、单词破、单词裹、空白省略号、字符与html显示
  <!DOCTYPE html>
  <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>换行之单词破、单词裹、空白省略号、字符与html显示</title>
      <style>
        *{
          margin: 0;
          padding: 10px;
        }
        html{
          font-size: 20px;
        }
        .commonA{
          margin: 0 auto;
          width: 800px;
          background: gray;
          margin-top: 20px;
        }
        .A1{
          word-break: normal;
        }
        .A2{
          word-break: break-all;/* 1/2,单词内部-任意-换行 */
        }
        .A3{
          word-break: keep-all;/* 半角空格/连字符-换行 */
        }
        .commonB{
          margin: 0 auto;
          width: 900px;
          background: gray;
          margin-top: 20px;
        }
        .B1{
          word-wrap: normal;/* 断字点换行 */
        }
        .B2{
          word-wrap: break-word;/* 2/2,单词内部-适当-换行 */
        }
        .commonC{
          margin: 0 auto;
          width: 1000px;
          background: gray;
          margin-top: 20px;
        }
        .C1{
          white-space: normal;/* 超宽换行 */
        }
        .C2{
          white-space: nowrap;/* 超宽不换行 */
        }
        .C3{
          white-space: pre-line;
        }
        .C4{
          white-space: pre;
        }
        .C5{
          white-space: pre-wrap;
        }
        .commonD{
          margin: 0 auto;
          width: 1000px;
          background: gray;
          margin-top: 20px;
        }
        .D1{
          /* 单行显示省略号!!! */
          width: 1000px;
          white-space: nowrap; /* 文本不换行 */
          text-overflow: ellipsis; /* 超宽,显示... */
          overflow: hidden;
        }
        .D2{
          /* 多行显示省略号 */
          width: 1000px; /* :style="{width:scope.row.changeNumber>1?'auto':'100%'}" */
          display: -webkit-box; /* 弹性盒子 */
          -webkit-box-orient: vertical; /* 子元素垂直排列 */
          -webkit-line-clamp: 2; /* 超出2行,显示... */
          overflow: hidden;
          /* 以下,附加内容 */
          line-height: 40px; /* 因为用这个样式*{padding: 10px;},所以需要这行代码,否则下一行的上半部分会显示 */
        }
        .commonE{
          margin: 0 auto;
          width: 1100px;
          background: gray;
          margin-top: 20px;
        }
        .commonZ{
          padding-bottom: 30px;
        }
      </style>
    </head>
    <body>
      <p>正常情况下,自然换行。因受别的样式影响,需要手动换行。两个行内元素,比如span,可以实现半包围效果!!!</p>
      <p>
        <span style="color:red;">半包围效果-被包围者</span>
        <span style="color:green;">半包围效果-包围者。包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者包围者</span>
      </p>
      <div class="commonA">1、单词破,https://www.w3school.com.cn/cssref/pr_word-break.asp</div>
      <div class="commonA A1">A1、word-break: normal;使用浏览器默认的换行规则</div>
      <div class="commonA A2">A2、word-break: break-all;1/2,允许在单词内换行。换行,多种,此种常用!!!</div>
      <div class="commonA A3">A3、word-break: keep-all;只能在半角空格或连字符处换行</div>
      <div class="commonB">2、单词裹,https://www.w3school.com.cn/cssref/pr_word-wrap.asp</div>
      <div class="commonB B2">B1、2003年首次出现的非标准属性word-wrap(现在可能无效!!!),与,</div>
      <div class="commonB B1">a、word-wrap: normal;只在允许的断字点换行(浏览器保持默认处理)</div>
      <div class="commonB B2">b、word-wrap: break-word;2/2,在长单词或URL地址内部进行换行</div>
      <div class="commonB B2">B2、2012年首次出现的标准属性overflow-wrap(现在绝对有效!!!),是同一属性的不同名称</div>
      <div class="commonB B1">a、overflow-wrap: normal;只在允许的断字点换行(浏览器保持默认处理)</div>
      <div class="commonB B2">b、overflow-wrap: break-word;2/2,在长单词或URL地址内部进行换行</div>
      <div class="commonC">3、空白,https://www.w3school.com.cn/cssref/pr_text_white-space.asp</div>
      <div class="commonC">“空白符”是由空格键产生的,“换行符”是由Enter键产生的</div>
      <div class="commonC C1">C1、white-space: normal;--合并空白符,不留换行符,超宽换行。</div>
      <div class="commonC C2">C2、white-space: nowrap;--合并空白符,不留换行符,超宽不换行,br除外。不换行,两种,此种常用</div>
      <div class="commonC C3">C3、white-space: pre-line;合并空白符,保留换行符,超宽换行。</div>
      <div class="commonC C4">C4、white-space: pre;-----保留空白符,保留换行符,超宽不换行。不换行,两种,此种不常用</div>
      <div class="commonC C5">C5、white-space: pre-wrap;保留空白符,保留换行符,超宽换行。</div>
      <div class="commonD">4、空白案例</div>
      <div class="commonD">D1、相关案例-单行省略号</div>
      <div class="commonD D1">
        单行-省-略-号1;单行-省-略-号2;单行-省-略-号3;单行-省-略-号4;单行-省-略-号5;单行-省-略-号6;单行-省-略-号7;
      </div>
      <div class="commonD">D2、无关案例-多行省略号</div>
      <div class="commonD D2">
        多行-省-略-号1;多行-省-略-号1;多行-省-略-号1;多行-省-略-号1;多行-省-略-号1;多行-省-略-号1;多行-省-略-号1;
        多行-省-略-号2;多行-省-略-号2;多行-省-略-号2;多行-省-略-号2;多行-省-略-号2;多行-省-略-号2;多行-省-略-号2;
        多行-省-略-号3;多行-省-略-号3;多行-省-略-号3;多行-省-略-号3;多行-省-略-号3;多行-省-略-号3;多行-省-略-号3;
      </div>
      <div class="commonE">
        <div>5、换行、回车、空格</div>
        <pre style="line-height: 40px;">
          (1)换行符,在html页面的textarea标签里,按回车键,用js获取  
            A、Windows:\r\n      
            B、macOS/Linux:\n     
            C、根据回车,切分textarea里的文字为数组:text.split(/[(\r\n)\r\n]+/)  
            D、在html中,替换回车为br标签:element.innerHTML=text.replace(/[(\r\n)\r\n]+/g,"<\br>");  
          (2)回车符\r,在前端编程中,一般不会单独出现      
          (3)用空格键打出来的多个空格,只显示1个     
          (4)以下是强制空格,多个空格,不会被合并     
            A、JS:“\xa0”      
            B、html:“&-nbsp;”   
        </pre>
      </div>
      <div class="commonE">
        <div>6、强制分页(打印或 PDF 导出)</div>
        <div>
          <div>(1)page-break-before: always; 强制在当前元素之前插入分页符</div>
          <div>(2)page-break-after: always; 强制在当前元素之后插入分页符</div>
        </div>
      </div>
      <div class="commonZ"></div>
    </body>
  </html>                             
21、html和css中的特殊符号或图标
  <html>
    <head>
      <style type="text/css">
        table{
          border-collapse: collapse;
        }
        table tr th,table tr td{
          border:1px solid grey;
          padding: 5px;
        }
        table tr td.color{
          color: green;
        }
        table tr td.empty::before {
          content: "\261E";
          display: inline-block;
          margin-right: 5px;
        }
      </style>
    </head>
    <body>
      <table>
        <thead>
          <tr>
            <th colspan="5">百度:在前端编程中&-nbsp;和&-#160;的区别</td>
          </tr>
          <tr>
            <th colspan="5">HTML代码和CSS代码来源:https://unicode-table.com/cn/</td>
          </tr>
          <tr>
            <th colspan="5">HTML代码和CSS代码来源:https://symbl.cc/cn/html-entities/</td>
          </tr>
          <tr>
            <th colspan="5">实体和html代码,去掉第2个字符“-”,就显示为图形</td>
          </tr>
          <tr>
            <th>图形</th>
            <th>实体</th>
            <th>html代码</th>
            <th>css代码</th>
            <th>css示例</th>
          </tr>
        </thead>
        <tbody>
          <tr class="color">
            <td class="color">  </td>
            <td class="color">&-nbsp;</td>
            <td class="color">&-#160;</td>
            <td class="color">\00A0</td>
            <td class="empty"></td>
          </tr>
          <tr>
            <td class="color"><</td>
            <td class="color">&-lt;</td>
            <td class="color">&-#60;</td>
            <td class="color">\003C</td>
            <td class="empty"></td>
          </tr>
          <tr>
            <td class="color">></td>
            <td class="color">&-gt;</td>
            <td class="color">&-#62;</td>
            <td class="color">\003E</td>
            <td class="empty"></td>
          </tr>
          <tr>
            <td class="color">&</td>
            <td class="color">&-amp;</td>
            <td class="color">&-#38;</td>
            <td class="color">\0026</td>
            <td class="empty"></td>
          </tr>
        </tbody>
      </table>
    </body>
  </html>
22、龙棋
  (1)用div+css实现
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>龙棋</title>
        <style>
          body {
            display: flex;
            justify-content: center;
            align-items: center;
            height: 100vh;
            margin: 0;
          }
          /* 以下,3个正方形 */
          .small .outer {
            width: 200px;
            height: 200px;
            border: 1px solid black;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #EEEEEE;
          }
          .small .middle {
            width: 150px;
            height: 150px; 
            border: 1px solid black;
            display: flex;
            justify-content: center;
            align-items: center;
          }
          .small .inner {
            width: 100px;
            height: 100px;
            border: 1px solid black;
            position: relative; /* 为连接线提供定位基准 */
          }
          /* 以下,所有连接线的样式 */
          .small .line {
            position: absolute;
            background-color: black;
            transform-origin: 0 0;
          }
          /* 以下,上方3条连接线 */   
          .small .top-left-line {/* 上左连接线 */
            width: 71px;
            height: 1px;
            top: 0;
            left: 0;
            transform: rotate(-135deg);
          }     
          .small .top-mid-line {/* 上中连接线 */
            width: 50px;
            height: 1px;
            top: -50px;
            left: 50px;
            transform: rotate(90deg);
          }    
          .small .top-right-line {/* 上右连接线 */
            width: 71px;
            height: 1px;
            top: -50px;
            left: 150px;
            transform: rotate(135deg);
          }
          /* 以下,中间2条连接线 */     
          .small .mid-left-line {/* 中左连接线 */
            width: 50px;
            height: 1px;
            top: 50px;
            left: -50px;
            transform: rotate(0deg);
          }   
          .small .mid-right-line {/* 中右连接线 */
            width: 50px;
            height: 1px;
            top: 50px;
            left: 100px;
            transform: rotate(0deg);
          }
          /* 以下,下方3条连接线 */     
          .small .bottom-left-line {/* 下左连接线 */
            width: 71px;
            height: 1px;
            top: 100px;
            left: 0;
            transform: rotate(135deg);
          }     
          .small .bottom-mid-line {/* 下中连接线 */
            width: 50px;
            height: 1px;
            top: 100px;
            left: 50px;
            transform: rotate(90deg);
          }      
          .small .bottom-right-line {/* 下右连接线 */
            width: 71px;
            height: 1px;
            top: 100px;
            left: 100px;
            transform: rotate(45deg);
          } 
          /* 以下,3个正方形 */
          .big .outer {
            width: 800px;
            height: 800px;
            border: 1px solid black;
            display: flex;
            justify-content: center;
            align-items: center;
            background: #EEEEEE;
          }
          .big .middle {
            width: 600px;
            height: 600px; 
            border: 1px solid black;
            display: flex;
            justify-content: center;
            align-items: center;
          }
          .big .inner {
            width: 400px;
            height: 400px;
            border: 1px solid black;
            position: relative; /* 为连接线提供定位基准 */
          }
          /* 以下,所有连接线的样式 */
          .big .line {
            position: absolute;
            background-color: black;
            transform-origin: 0 0;
          }
          /* 以下,上方3条连接线 */   
          .big .top-left-line {/* 上左连接线 */
            width: 284px;
            height: 1px;
            top: 0;
            left: 0;
            transform: rotate(-135deg);
          }     
          .big .top-mid-line {/* 上中连接线 */
            width: 200px;
            height: 1px;
            top: -200px;
            left: 200px;
            transform: rotate(90deg);
          }    
          .big .top-right-line {/* 上右连接线 */
            width: 284px;
            height: 1px;
            top: -200px;
            left: 600px;
            transform: rotate(135deg);
          }
          /* 以下,中间2条连接线 */     
          .big .mid-left-line {/* 中左连接线 */
            width: 200px;
            height: 1px;
            top: 200px;
            left: -200px;
            transform: rotate(0deg);
          }   
          .big .mid-right-line {/* 中右连接线 */
            width: 200px;
            height: 1px;
            top: 200px;
            left: 400px;
            transform: rotate(0deg);
          }
          /* 以下,下方3条连接线 */     
          .big .bottom-left-line {/* 下左连接线 */
            width: 284px;
            height: 1px;
            top: 400px;
            left: 0;
            transform: rotate(135deg);
          }     
          .big .bottom-mid-line {/* 下中连接线 */
            width: 200px;
            height: 1px;
            top: 400px;
            left: 200px;
            transform: rotate(90deg);
          }      
          .big .bottom-right-line {/* 下右连接线 */
            width: 284px;
            height: 1px;
            top: 400px;
            left: 400px;
            transform: rotate(45deg);
          }
        </style>
      </head>
      <body> 
        <div class="outer">
          <div class="middle">
            <div class="inner">
              <div class="line top-left-line"></div>
              <div class="line top-mid-line"></div>
              <div class="line top-right-line"></div>
              <div class="line mid-left-line"></div>
              <div class="line mid-right-line"></div>
              <div class="line bottom-left-line"></div>
              <div class="line bottom-mid-line"></div>
              <div class="line bottom-right-line"></div>
            </div>
          </div>
        </div>
      </body>
    </html>
    <script>    
      var isSmall = true;
      var element = document.getElementsByTagName("body")[0];
      element.classList.add(isSmall ? "small" : "big");    
    </script>
  (2)用svg实现
    <!DOCTYPE html>
    <html lang="en">
      <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>龙棋</title>
        <style>
          .center{
            display: flex;
            justify-content: center;
            align-items: center;
            margin-top: 150px;
          }
          .small{
            transform: scale(0.5);
          }
          .big{
            transform: scale(1.8);
          }
        </style>
      </head>
      <body class="center">
        <!-- 
          第1个path绘制并填充灰色背景,
          第2个path绘制并填充黑色内容,以后的path绘制并填充灰色小格,小格间漏出黑色内容,便是龙棋的棋盘 
        -->
        <svg c 
          xmlns="http://www.w3.org/2000/svg" 
          xmlns:xlink="http://www.w3.org/1999/xlink" 
          version="1.1" width="461px" height="482px" 
        >
          <!-- 
          <g><path style="opacity:1" fill="#999999" d="M -0.5,-0.5 C 153.167,-0.5 306.833,-0.5 460.5,-0.5C 460.5,160.167 460.5,320.833 460.5,481.5C 306.833,481.5 153.167,481.5 -0.5,481.5C -0.5,320.833 -0.5,160.167 -0.5,-0.5 Z"/></g> 
          -->
          <g><path style="opacity:1" fill="#000000" d="M 23.5,38.5 C 158.167,38.5 292.833,38.5 427.5,38.5C 427.5,173.167 427.5,307.833 427.5,442.5C 292.833,442.5 158.167,442.5 23.5,442.5C 23.5,307.833 23.5,173.167 23.5,38.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 28.5,41.5 C 93.3312,40.5009 158.331,40.1675 223.5,40.5C 223.5,56.5 223.5,72.5 223.5,88.5C 174.166,88.6667 124.832,88.5 75.5,88C 59.9652,72.2982 44.2986,56.7982 28.5,41.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 225.5,40.5 C 291.168,40.3333 356.834,40.5 422.5,41C 406.833,56.6667 391.167,72.3333 375.5,88C 325.501,88.5 275.501,88.6667 225.5,88.5C 225.5,72.5 225.5,56.5 225.5,40.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 25.5,40.5 C 41.4652,55.9649 57.2986,71.6315 73,87.5C 73.5,138.499 73.6667,189.499 73.5,240.5C 57.5,240.5 41.5,240.5 25.5,240.5C 25.5,173.833 25.5,107.167 25.5,40.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 424.5,40.5 C 425.499,106.998 425.832,173.665 425.5,240.5C 409.5,240.5 393.5,240.5 377.5,240.5C 377.333,189.499 377.5,138.499 378,87.5C 393.702,71.9652 409.202,56.2986 424.5,40.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 78.5,91.5 C 126.664,90.5012 174.997,90.1678 223.5,90.5C 223.5,106.5 223.5,122.5 223.5,138.5C 190.832,138.667 158.165,138.5 125.5,138C 109.965,122.298 94.2986,106.798 78.5,91.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 225.5,90.5 C 274.501,90.3333 323.501,90.5 372.5,91C 356.833,106.667 341.167,122.333 325.5,138C 292.168,138.5 258.835,138.667 225.5,138.5C 225.5,122.5 225.5,106.5 225.5,90.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 75.5,90.5 C 91.4652,105.965 107.299,121.632 123,137.5C 123.5,171.832 123.667,206.165 123.5,240.5C 107.5,240.5 91.5,240.5 75.5,240.5C 75.5,190.5 75.5,140.5 75.5,90.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 374.5,90.5 C 375.499,140.331 375.832,190.331 375.5,240.5C 359.5,240.5 343.5,240.5 327.5,240.5C 327.333,206.165 327.5,171.832 328,137.5C 343.702,121.965 359.202,106.299 374.5,90.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 125.5,140.5 C 192.167,140.5 258.833,140.5 325.5,140.5C 325.5,207.167 325.5,273.833 325.5,340.5C 258.833,340.5 192.167,340.5 125.5,340.5C 125.5,273.833 125.5,207.167 125.5,140.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 25.5,242.5 C 41.5,242.5 57.5,242.5 73.5,242.5C 73.6667,291.834 73.5,341.168 73,390.5C 57.3333,406.167 41.6667,421.833 26,437.5C 25.5,372.501 25.3333,307.501 25.5,242.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 75.5,242.5 C 91.5,242.5 107.5,242.5 123.5,242.5C 123.667,275.168 123.5,307.835 123,340.5C 107.333,356.167 91.6667,371.833 76,387.5C 75.5,339.168 75.3333,290.834 75.5,242.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 327.5,242.5 C 343.5,242.5 359.5,242.5 375.5,242.5C 375.667,291.834 375.5,341.168 375,390.5C 359.333,374.833 343.667,359.167 328,343.5C 327.5,309.835 327.333,276.168 327.5,242.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 377.5,242.5 C 393.5,242.5 409.5,242.5 425.5,242.5C 425.667,308.501 425.5,374.501 425,440.5C 409.333,424.833 393.667,409.167 378,393.5C 377.5,343.168 377.333,292.834 377.5,242.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 122.5,342.5 C 156.167,342.5 189.833,342.5 223.5,342.5C 223.5,358.5 223.5,374.5 223.5,390.5C 174.166,390.667 124.832,390.5 75.5,390C 91.3685,374.299 107.035,358.465 122.5,342.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 225.5,342.5 C 258.835,342.333 292.168,342.5 325.5,343C 341.167,358.667 356.833,374.333 372.5,390C 323.501,390.5 274.501,390.667 225.5,390.5C 225.5,374.5 225.5,358.5 225.5,342.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 72.5,392.5 C 122.833,392.5 173.167,392.5 223.5,392.5C 223.5,408.5 223.5,424.5 223.5,440.5C 157.499,440.667 91.4992,440.5 25.5,440C 41.3685,424.299 57.0351,408.465 72.5,392.5 Z"/></g>
          <g><path style="opacity:1" fill="#555555" d="M 225.5,392.5 C 275.501,392.333 325.501,392.5 375.5,393C 391.167,408.667 406.833,424.333 422.5,440C 356.834,440.5 291.168,440.667 225.5,440.5C 225.5,424.5 225.5,408.5 225.5,392.5 Z"/></g>
        </svg>
      </body>
    </html> 
    <script>   
      var isSmall = true;
      var element = document.getElementsByTagName("svg")[0];
      element.classList.add(isSmall ? "small" : "big"); 
    </script> 
23、CSS伪类伪元素选择器
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <title>伪类与伪元素</title>
      <style>
        p{
          font-size: 16px;
        }
        p::first-letter {
          font-size: 2em;
          color: blue;
        }
        p::first-line {
          color:rgb(185, 130, 130);
        }
        p::after {
          content: '';
          display: inline-block;
          width: 10px;
          height: 10px;
          background:rgb(197, 51, 51);
        }
        ::selection { /* 使所选文本在蓝色背景上显示为红色 */
          color: #FFFFFF;
          background: #000000;
        }
        h3:hover{
          background-color: #777777;
        }
        div:nth-child(2){/* 选中父元素的第二个子元素,且是div */
          background-color: #888888;
        }
        div:nth-of-type(2){/* 选中父元素的第二个div子元素,跳过其他子元素,只统计div */
          background-color: #999999;
        }
      </style>
    </head>
    <body>
      <p style="width:500px">
        P元素的第1个字符p::first-letter,
        P元素的第1行p::first-line,
        p元素的选中::selection,
        p元素的最后p::after,
      </p>
      <pre>
        <h3>1、基本选择器</h3>
        <div> 
          注、div:nth-child(2),这是父元素的第二个子元素,且是div
          (1)*:通配选择器
          (2)E:元素选择器 选择指定类型的HTML元素
          (3)#id:ID选择器 选择指定ID属性值为“id”的任意类型元素
          (4).class:类选择器
          (5)selector1,selectorN:群组选择器
        </div>
        <h3>2、层次选择器</h3>
        <div>
          注、div:nth-of-type(2),这是父元素的第二个div子元素,跳过其他子元素,只统计div
          (1)E F:后代选择器
          (2)E>F:子选择器
          (3)E+F:相邻兄弟选择器
          (4)E~F:通用选择器,选择E元素后的所有匹配的F元素
        </div>
        <h3>3、伪元素选择器--针对标签内的一小部分</h3>
        <div>
          示例1,选择标签的特定部分
          (1)p::first-letter,选择每个p元素的首字母
          (2)p::first-line,选择每个p元素的首行
          (3)p::selection,选择用户选择的元素部分
          示例2,生成虚拟内容
          (1)p::after,在每个p元素内部的后面插入内容
          (2)p::before,在每个p元素内部的前面插入内容
        </div>
        <h3>4、伪类选择器--针对整个标签</h3>
        <div>
          示例1,选择特定标签
            A、子元素
              (1)p:first-child,p作为子元素第1个出现时,则选中p元素
              (2)p:nth-child(2),p作为子元素第2个出现时,则选中p元素
              (3)p:last-child,p作为子元素倒数第1个出现时,则选中p元素
              (4)p:nth-last-child(2),p作为子元素倒数第2个出现时,则选中p元素
              (5)p:only-child,p作为子元素唯一1个出现时,则选中p元素
            B、元素类型
              (1)p:first-of-type,p作为子元素第1次出现时,则选中p元素
              (2)p:nth-of-type(2),p作为子元素第2次出现时,则选中p元素
              (3)p:last-of-type,p作为子元素倒数第1次出现时,则选中p元素
              (4)p:nth-last-of-type(2),p作为子元素倒数第2次出现时,则选中p元素
              (5)p:only-of-type,p作为子元素唯一1种出现时,则选中p元素
            C、元素状态
              (1)p:empty,p元素没有子元素,则选中p元素
              (2)p:lang(it),p元素的lang属性值以"it"开头,则选中p元素;(span lang="fr")这里包含一些法语(/span)
              (3):not(p),不是p元素,则选中该元素
              (4):root,是根元素,则选中该元素
              (5)#news:target,选择当前活动的#news元素(单击包含该锚名称的 URL)
          示例2,选择标签的特定状态
            A、a标签
            (1)a:link,选择所有未被访问的链接
            (2)a:hover,选择鼠标悬停其上的链接,:hover选择器可用于所有元素,不只是链接
            (3)a:active,选择活动的链接,鼠标按下去之后、弹起来之前的状态
            (4)a:visited,选择所有已访问的链接
            B、input标签
            (1)input:checked,选择每个被选中的input元素
            (2)input:disabled,选择每个被禁用的input元素
            (3)input:enabled,选择每个已启用的input元素
            (4)input:focus,选择获得焦点的input元素
            (5)input:in-range,选择具有指定范围内的值的input元素
            (6)input:invalid,选择所有具有无效值的input元素
            (7)input:optional,选择不带"required"属性的input元素
            (8)input:out-of-range,选择值在指定范围之外的input元素
            (9)input:read-only,选择指定了"readonly"属性的input元素
            (10)input:read-write,选择不带"readonly"属性的input元素
            (11)input:required,选择指定了"required"属性的input元素
            (12)input:valid,选择所有具有有效值的input元素
        </div>
      </pre>
    </body>
  </html>
24、底部中间三角形
  <!DOCTYPE html>
  <html lang="zh-CN">
    <head>
      <title>底部中间三角</title>
      <meta charset="utf-8">
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <style>
        .detail {
          background: #726e6e;
          border-radius: 6px;
          padding: 10px;
          margin: 20px;
        }
        .detail .border {
          border: 50px solid transparent;/* 边框宽度; 边框颜色; */
          border-top-color: #ccc;
          border-bottom-color: #ccc;
          border-left-color: gray;
          border-right-color: gray;
          border-radius: 6px;
        }
        .detail .zero {
          width: 0px;
          height: 0px;
        }
        .detail .noZero {
          width: 300px;
          height: 100px;
        }
        /* 以上是上图,以下是下图 -------------------------- */
        .arrow-box {
          position: relative;
          height: 160px;
          background: #ccc;
          border: 2px solid gray;
          border-radius: 6px;
        }
        /* 以下,向下三角箭头的-背景、内容,都是宽高为0,边框透明,定位在盒子底部*/
        .arrow-box::before, .arrow-box::after{
          height: 0;
          width: 0;
          content: "";
          border: solid transparent;
          position: absolute;
          top: 100%;
          left: 50%;/* 向右移动50% */
        }
        /* 以下,向下三角箭头的-背景,是伪元素的上边框 */
        .arrow-box::before { /* 箭头背景颜色 */
          border-width: 15px; /* 边框宽度;左、右边框宽度各15px,即向下三角箭头的背景宽度为30px */
          border-color: transparent; /* 边框颜色; */
          border-top-color: gray; /* 上边框颜色; */
          margin-left: -15px; /* 向下三角箭头的背景向左移动15px */
        }
        /* 以下,向下三角箭头的-内容,是伪元素的上边框 */
        .arrow-box::after { /* 箭头内部颜色 */
          border-width: 12px; /* 边框宽度;左、右边框宽度各12px,即向下三角箭头的内容宽度为24px */
          border-color: transparent; /* 边框颜色; */
          border-top-color: #ccc; /* 上边框颜色; */
          margin-left: -12px; /* 向下三角箭头的内容向左移动12px */
        }
        /* 说明:
            1、2个伪类元素的内容宽都为0,
            2、只能显示4个边框,每个边框都是指向中心的三角形
        */
      </style>
    </head>
    <body>
      <div class="detail">
        <div>(1)width、height都为0时,上下左右的border都是三角形。如下图</div>
        <div class="border zero"></div>
        <br/>
        <br/>
        <div>(2)width、height都不为0时,上下左右的border都是梯形。如下图</div>
        <div class="border noZero"></div>
      </div>
      <div class="arrow-box">底部中间三角。来源,https://www.wetools.com/css-arrow</div>
    </body>
  </html>
25、各种灰色
  <!DOCTYPE html>
  <html>
    <head>
      <meta charset="UTF-8">
      <title>各种灰色</title>
    </head>
    <style type="text/css">
      body {
          font-family: 'Microsoft YaHei', sans-serif;
          max-width: 1200px;
          margin: 0 auto;
          background-color: #EEEEEE
      }
      .category {
          background-color: #FFFFFF;
          padding: 20px;
          border-radius: 8px;
          margin-bottom: 30px;
          box-shadow: 0 2px 5px #AAAAAA
      }
      h2 {
          color: #444444;
          border-left: 4px solid #666666;
          padding-left: 10px;
          margin-top: 0;
      }
      .grid {
          display: grid;
          grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
          gap: 15px;
      }
      .card {
          height: 100px;
          border-radius: 6px;
          display: flex;
          flex-direction: column;
          justify-content: center;
          align-items: center;
          box-shadow: 0 2px 3px #AAAAAA;
          font-family: monospace;
          font-weight: bold;
          font-size: 16px;
          margin-bottom: 10px;
      }
      .white {
          color: #FFFFFF;
      }
    </style>
    <body>
        <div id="styleId">
        <p>以下,十六进制颜色展示</p>
        <p>颜色总数,16的6次方,即1677.7216万</p>
        <p>牛顿七色,红橙黄绿“蓝靛”紫,变化不均匀</p>
        <p>现代七色,红橙黄绿“青蓝”紫,过渡更平滑</p>
        <div class="category">
            <h2>1、1组-相同字母</h2>
            <div class="grid">
            <div v-for="letter in letters" :key="letter" class="card" :style="'background-color:#'+letter.repeat(6)">
                #{{ letter.repeat(6) }}
            </div>
            </div>
        </div>
        <div class="category">
            <h2>2、1组-相同数字</h2>
            <div class="grid white">
            <div v-for="number in numbers" :key="number" class="card" :style="'background-color:#'+number.repeat(6)">
                #{{ number.repeat(6) }}
            </div>
            </div>
        </div>
        <div class="category">
            <h2>3、2组-相同数字/字母</h2>
            <div class="grid white">
            <div v-for="(two, index) in sixteen" :key="two" v-if="index < sixteen.length - 1"
                :style="'background-color:#'+(sixteen[index].repeat(3)+sixteen[index+1].repeat(3))" class="card">
                #{{ sixteen[index].repeat(3)+sixteen[index+1].repeat(3)}}
            </div>
            </div>
        </div>
        <div class="category">
            <h2>4、3组-相同数字/字母</h2>
            <div class="grid white">
            <div v-for="(two, index) in sixteen" :key="two" v-if="index < sixteen.length - 2"
                :style="'background-color:#'+(sixteen[index].repeat(2)+sixteen[index+1].repeat(2)+sixteen[index+2].repeat(2))" class="card">
                #{{ sixteen[index].repeat(2)+sixteen[index+1].repeat(2)+sixteen[index+2].repeat(2) }}
            </div>
            </div>
        </div>
        <div class="category">
            <h2>5、3组-相同数字+相同字母</h2>
            <div v-for="letter in letters" :key="letter" class="grid white">
              <div v-for="number in numbers" :key="number" class="card" :style="'background-color:#'+(number+letter).repeat(3)">
                  #{{ (number + letter).repeat(3) }}
              </div>
            </div>
        </div>
        <div class="category">
            <h2>6、3组-相同字母+相同数字</h2>
            <div v-for="letter in letters" :key="letter" class="grid">
              <div v-for="number in numbers" :key="number" class="card" :style="'background-color:#'+(letter+number).repeat(3)">
                  #{{ (letter + number).repeat(3) }}
              </div>
            </div>
        </div>
        <div class="category">
            <h2>7、7种基础颜色</h2>
            <div class="grid">
            <div v-for="(color, index) in seventy" :key="index" :style="'background-color:#'+color" class="card">
                #{{ color }}{{ sevenObj[color]||'' }}
            </div>
            </div>
        </div>
        </div>
    </body>
  </html>
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
  <script type="text/javascript">
    var vm = new Vue({
        el: '#styleId',
        data: {
            letters: ['A', 'B', 'C', 'D', 'E', 'F'],
            numbers: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'],
            sixteen: ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'],
            seventy: [
              'FFE5E5', 'FFCCCC', 'FFB2B2', 'FF9999', 'FF0000', 'E60000', 'CC0000', 'B20000', '990000', '800000',
              'FFE9CC', 'FFD699', 'FFC266', 'FFAF33', 'FFA500', 'E59400', 'CC8400', 'B27300', '996300', '805200',
              'FFFFCC', 'FFFF99', 'FFFF66', 'FFFF33', 'FFFF00', 'E6E600', 'CCCC00', 'B2B200', '999900', '808000',
              'CCFFCC', '99FF99', '66FF66', '33FF33', '00FF00', '00E600', '00CC00', '00B200', '009900', '008000',
              'CCFFFF', '99FFFF', '66FFFF', '33FFFF', '00FFFF', '00E6E6', '00CCCC', '00B2B2', '009999', '008080',
              'CCCCFF', '9999FF', '6666FF', '3333FF', '0000FF', '0000E6', '0000CC', '0000B2', '000099', '000080',
              'E5CCE5', 'CC99CC', 'B266B2', '993399', '800080', '730073', '660066', '590059', '4D004D', '400040',
            ],
            sevenObj: {'FF0000':'红', 'FFA500':'橙', 'FFFF00':'黄', '00FF00':'绿', '00FFFF':'青', '0000FF':'蓝', '800080':'紫',},
        },
    });
  </script>

八、canvas原生水印,可配置,可预览,可下载
  <!DOCTYPE html>
  <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>水印生成器</title>
      <style>
        body {
          font-family: Arial, sans-serif;
          max-width: 800px;
          margin: 0 auto;
          padding: 20px;
          background-color: #f5f5f5;
        }
        .container {
          background-color: white;
          padding: 20px;
          border-radius: 8px;
          box-shadow: 0 2px 10px rgba(0,0,0,0.1);
        }
        h1 {
          color: #333;
          text-align: center;
        }
        canvas {
          border: 1px solid #ddd;
          max-width: 100%;
          margin-top: 15px;
          background-color: #f9f9f9;
        }
        .controls {
          display: flex;
          flex-wrap: wrap;
          gap: 20px;
          margin-bottom: 15px;
        }
        .control-group {
          display: flex;
          flex-direction: column;
          flex-grow: 1;
          width: 200px;
        }
        label {
          margin-bottom: 5px;
          font-weight: bold;
          color: #555;
        }
        input[type="text"], input[type="range"], input[type="color"], input[type="file"], select {
          padding: 8px;
          border: 1px solid #ddd;
          border-radius: 4px;
          font-size: 14px;
          outline: none;
        }
        button {
          padding: 10px 15px;
          border: none;
          border-radius: 4px;
          cursor: pointer;
          font-size: 14px;
          font-weight: bold;
          transition: background-color 0.3s;
        }
        .slider-container {
          display: flex;
          align-items: center;
          gap: 10px;
        }
        .slider-container input{
          flex: 1;
        }
        .slider-value {
          min-width: 30px;
          text-align: center;
        }
        .checkbox-container {
          display: flex;
          align-items: center;
          gap: 8px;
          margin-top: 8px;
        }
        .canvas-container {
          display: flex;
          justify-content: center; /* 水平居中 */
          align-items: center; /* 垂直居中(如果父容器有固定高度) */
          width: 100%; /* 占满可用宽度 */
          padding: 10px 0; /* 上下留出一些空间 */
        }
      </style>
    </head>
    <body>
      <div class="container">
        <h1>水印生成器</h1>
        <div class="controls">
          <div class="control-group">
            <label for="uploadBtn">选择图片</label>
            <input type="file" id="uploadBtn" accept="image/*" style="padding: 4px;">
          </div>
          <div class="control-group">
            <label>图片尺寸选项</label>
            <div class="checkbox-container">
              <input type="checkbox" id="keepFixedSize">
              <label for="keepFixedSize">使用固定尺寸</label>
            </div>
          </div>
          <div class="control-group">
            <label for="colorPicker">水印颜色</label>
            <div class="slider-container">
              <input type="color" id="colorPicker" value="#888888" style="height:34px">
            </div>
          </div>
          <div class="control-group">
            <label for="angleSlider">倾斜角度</label>
            <div class="slider-container">
              <input type="range" id="angleSlider" min="-45" max="45" value="30">
              <span id="angleValue" class="slider-value">30°</span>
            </div>
          </div>
          <div class="control-group">
            <label for="opacitySlider">透明度</label>
            <div class="slider-container">
              <input type="range" id="opacitySlider" min="10" max="90" value="70">
              <span id="opacityValue" class="slider-value">70%</span>
            </div>
          </div>
          <div class="control-group">
            <label for="sizeSlider">字体大小</label>
            <div class="slider-container">
              <input type="range" id="sizeSlider" min="12" max="500" value="24">
              <span id="sizeValue" class="slider-value">24px</span>
            </div>
          </div>
          <div class="control-group">
            <label for="watermarkText">水印文字(最多20个字符)</label>
            <input type="text" id="watermarkText" placeholder="输入水印文字" value="公安部A级通缉令"  maxlength="20" >
          </div>
          <div class="control-group">
            <label for="fontFamily">字体家族</label>
            <select id="fontFamily">
              <option value="KaiTi">楷体</option>
              <option value="SimSun">宋体</option>
              <option value="SimHei">黑体</option>
              <option value="FangSong">仿宋</option>
              <option value="Microsoft YaHei">微软雅黑</option>
              <option value="Arial">Arial</option>
              <option value="Times New Roman">Times New Roman</option>
              <option value="Courier New">Courier New</option>
            </select>
          </div>
          <div class="control-group">
            <label for="fontStyle">字体样式</label>
            <select id="fontStyle">
              <option value="normal">正常</option>
              <option value="bold">加粗</option>
              <option value="italic">斜体</option>
              <option value="bold italic">加粗斜体</option>
            </select>
          </div>
        </div>
        <div class="controls">
          <button id="addWatermarkBtn">添加水印</button>
          <button id="downloadBtn">下载图片</button>
        </div>
        <div class="canvas-container">
          <canvas id="canvas"></canvas>
        </div>
      </div>
    </body>
  </html>     
  <script>
    document.addEventListener('DOMContentLoaded', function() {
      // 获取DOM元素
      const canvas = document.getElementById('canvas');
      const ctx = canvas.getContext('2d');
      const uploadBtn = document.getElementById('uploadBtn');
      const watermarkText = document.getElementById('watermarkText');
      const addWatermarkBtn = document.getElementById('addWatermarkBtn');
      const downloadBtn = document.getElementById('downloadBtn');
      const angleSlider = document.getElementById('angleSlider');
      const angleValue = document.getElementById('angleValue');
      const opacitySlider = document.getElementById('opacitySlider');
      const opacityValue = document.getElementById('opacityValue');
      const colorPicker = document.getElementById('colorPicker');
      const sizeSlider = document.getElementById('sizeSlider');
      const sizeValue = document.getElementById('sizeValue');
      const fontFamily = document.getElementById('fontFamily');
      const fontStyle = document.getElementById('fontStyle');
      const keepFixedSize = document.getElementById('keepFixedSize');
      const maxObj = {
        width: 600,
        height: 400
      };
      let currentImage = null;
      let originalFileName = '';
      let originalImageWidth = 0;
      let originalImageHeight = 0;
      // 核心:抽离的canvas尺寸调整函数
      function resizeCanvas() {
        if (keepFixedSize.checked) { // 使用固定尺寸maxObj
          let width = originalImageWidth;
          let height = originalImageHeight;
          const maxWidth = maxObj.width || 800;
          const maxHeight = maxObj.height || 600;
          const aspectRatio = width / height; // 原始宽高比
          const widthOver = width > maxWidth;
          const heightOver = height > maxHeight;
          // 确定基准
          // 宽高都超限:宽>高,以宽为基准;反之,以高为基准
          // 宽高只有1个超限:谁超限以谁为基准
          // 宽高都未超限:使用原图尺寸
          if (widthOver && heightOver) {
            // 宽高都超限:宽>高,以宽为基准
            if (aspectRatio > 1) {
              width = maxWidth;
              height = Math.round(width / aspectRatio);
            } else { // 反之,以高为基准
              height = maxHeight;
              width = Math.round(height * aspectRatio);
            }
          } else if (widthOver) {
            // 仅宽度超限:以maxWidth为基准缩放
            width = maxWidth;
            height = Math.round(width / aspectRatio);
          } else if (heightOver) {
            // 仅高度超限:以maxHeight为基准缩放
            height = maxHeight;
            width = Math.round(height * aspectRatio);
          } else {
            // 宽高都未超限:使用原始尺寸
            width = originalImageWidth;
            height = originalImageHeight;
          }
          // 设置canvas最终尺寸
          canvas.width = width;
          canvas.height = height;
        } else {
          // 不使用固定尺寸:使用原图尺寸
          canvas.width = originalImageWidth;
          canvas.height = originalImageHeight;
        }
      }
      // 更新倾斜角度
      angleSlider.addEventListener('input', () => {
        angleValue.textContent = `${angleSlider.value}°`;
      });
      // 更新透明度
      opacitySlider.addEventListener('input', () => {
        opacityValue.textContent = `${opacitySlider.value}%`;
      });
      // 更新字体大小
      sizeSlider.addEventListener('input', () => {
        sizeValue.textContent = `${sizeSlider.value}px`;
      });
      // 上传图片并显示在canvas中
      uploadBtn.addEventListener('change', function(e) {
        const file = e.target.files[0];
        if (!file) return;
        originalFileName = file.name;
        const reader = new FileReader();
        reader.onload = function(event) {
          const img = new Image();
          img.onload = function() {
            currentImage = img;
            originalImageWidth = img.width;
            originalImageHeight = img.height;
            // 调用抽离的函数设置尺寸
            resizeCanvas();
            ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
            uploadBtn.value = ''; //清空上传按钮的值,重新上传同一张图片,也可以触发change事件
            // 以下,与canvas尺寸相关!!!
              // 1、3种尺寸,图片尺寸、样式尺寸、绘制尺寸
              // 2、canvas默认标签尺寸,宽300px;高150px;坐标原点(0,0)位于画布左上角,向右是x轴正方向,向下是y轴正方向
              // 3、尺寸设置,js设置大于标签设置,同种设置时,最大尺寸大于尺寸
            // 以下,drawImage,在canvas的上下文中绘制图像,项目中最常使用5个参数,使用3、5、9个参数说明!!!
              // 1、使用3个参数,drawImage(img,x,y)。不缩放,不裁切;可能不完整
              //   (1)img:必填,图像源。这可以是一个
              //     A、HTMLImageElement,创建方式为new Image()和document.getElementById("myImage")
              //     B、HTMLCanvasElement,创建方式为document.createElement("canvas")和document.getElementById("myCanvas")
              //     C、HTMLVideoElement,创建方式为document.createElement("video")和document.getElementById("myVideo")
              //   (2)x和y:必填,图像在画布上的起始坐标
              //     A、直接使用原图的自然宽高,即img.naturalWidth、img.naturalHeight
              //     B、如果原图比Canvas大,超出部分会被裁剪,不会自动缩放
              //     C、如果原图比Canvas小,剩余区域保持空白,不会自动拉伸
              // 2、使用5个参数,drawImage(img, x, y, width, height)。只缩放,不裁切;可能变形
              //   (1)图片会被“强制缩放”至指定的width和height尺寸
              //   (2)若width/height比例与原图不一致,图像会发生变形(非等比例缩放)
              //   (3)如需保持宽高比,需手动计算并设置合适的width或height值
              // 3、使用9个参数,drawImage(img,sx,sy,sWidth,sHeight,dx,dy,dWidth,dHeight)。既缩放,又裁切;可能变形,可能不完整
              //   (1)前4个参数(sx,sy,sWidth,sHeight)用于裁切原图 
              //     A、从原图的(sx,sy)坐标开始,截取宽为sWidth、高为sHeight的区域
              //     B、超出原图范围的部分会被忽略
              //   (2)后4个参数(dx,dy,dWidth,dHeight)用于定义裁切区域在画布上的绘制位置和尺寸
              //     A、将裁切后的区域绘制到画布的(dx, dy)坐标处
              //     B、并缩放至宽dWidth、高dHeight的尺寸
              //   (3)若sWidth/sHeight与dWidth/dHeight的比例不一致
              //     A、裁切后的区域会被拉伸或压缩,可能导致图像变形
              //     B、如需保持比例,需手动计算两组尺寸的比例并保持一致
              //   (4)此模式同时实现了“裁切原图局部”和“缩放绘制”的功能,适用于雪碧图(精灵图)展示、图片局部截取等场景
          };
          img.src = event.target.result;
        };
        reader.readAsDataURL(file);
      });
      // 添加水印
      addWatermarkBtn.addEventListener('click', function() {
        if (!currentImage) {
          alert('请先上传图片');
          return;
        }
        const text = watermarkText.value.trim();
        if (!text) {
          alert('请输入水印文字');
          return;
        }
        // 调用抽离的函数重新设置尺寸
        resizeCanvas();
        // 重新绘制原始图片
        ctx.drawImage(currentImage, 0, 0, canvas.width, canvas.height);
        // 获取水印参数
        const angle = parseInt(angleSlider.value);
        const opacity = parseInt(opacitySlider.value) / 100;
        const color = colorPicker.value;
        const fontSize = parseInt(sizeSlider.value);
        const fontFamilyValue = fontFamily.value;
        const fontStyleValue = fontStyle.value;
        // 构建字体字符串
        const fontString = `${fontStyleValue} ${fontSize}px ${fontFamilyValue}`;
        ctx.font = fontString;
        // 计算水印文字宽度
        const textWidth = ctx.measureText(text).width;
        // 计算水印间距
        const spacingX = textWidth * 1.1;
        const spacingY = fontSize * 3.1;
        // 设置水印样式
        const rgbColor = hexToRgb(color);
        ctx.fillStyle = `rgba(${rgbColor.r}, ${rgbColor.g}, ${rgbColor.b}, ${opacity})`;
        ctx.textAlign = 'center';
        ctx.textBaseline = 'middle';
        // 保存当前画布状态
        ctx.save();
        // 计算旋转后的边界
        const rad = angle * Math.PI / 180;
        const cos = Math.cos(rad);
        const sin = Math.sin(rad);
        // 计算需要覆盖的区域
        const diagonal = Math.sqrt(canvas.width * canvas.width + canvas.height * canvas.height);
        const offsetX = diagonal / 2 - canvas.width / 2;
        const offsetY = diagonal / 2 - canvas.height / 2;
        // 在扩展的画布上绘制水印
        for (let x = -offsetX; x < diagonal; x += spacingX) {
          for (let y = -offsetY; y < diagonal; y += spacingY) {
            ctx.save();
            ctx.translate(x, y);
            ctx.rotate(rad);
            ctx.fillText(text, 0, 0);
            ctx.restore();
          }
        }
        // 恢复原始画布状态
        ctx.restore();
      });
      // 下载图片
      downloadBtn.addEventListener('click', function() {
        if (!currentImage) {
          alert('请先上传图片');
          return;
        }
        let fileExt = 'png';
        if (originalFileName) {
          const lastDotIndex = originalFileName.lastIndexOf('.');
          if (lastDotIndex > 0) {
            fileExt = originalFileName.substring(lastDotIndex + 1).toLowerCase();
            const validExtensions = ['jpg', 'jpeg', 'png', 'gif', 'webp'];
            if (!validExtensions.includes(fileExt)) {
              fileExt = 'png';
            }
          }
        }
        let downloadName = originalFileName || 'watermarked-image';
        if (originalFileName) {
          const lastDotIndex = originalFileName.lastIndexOf('.');
          if (lastDotIndex > 0) {
            downloadName = originalFileName.substring(0, lastDotIndex);
          }
        }
        let mimeType = 'image/png';
        if (fileExt === 'jpg' || fileExt === 'jpeg') {
          mimeType = 'image/jpeg';
        } else if (fileExt === 'gif') {
          mimeType = 'image/gif';
        } else if (fileExt === 'webp') {
          mimeType = 'image/webp';
        }
        const link = document.createElement('a');
        link.download = `${downloadName}.${fileExt}`;
        link.href = canvas.toDataURL(mimeType);
        link.click();
      });
      // 辅助函数:十六进制颜色转RGB
      function hexToRgb(hex) {
        hex = hex.replace('#', '');
        const r = parseInt(hex.substring(0, 2), 16);
        const g = parseInt(hex.substring(2, 4), 16);
        const b = parseInt(hex.substring(4, 6), 16);
        return { r, g, b };
      }
    });
  </script>
  
  

  

posted @ 2019-07-28 15:38  WEB前端工程师_钱成  阅读(3847)  评论(0)    收藏  举报