首页 > 其他分享 >使用fabric.js框选图片区域并生成svg图

使用fabric.js框选图片区域并生成svg图

时间:2024-03-28 15:13:07浏览次数:17  
标签:canvas const fabric svg js vals active obj name

仍然是 在图片上特定区域根据数值显示不同的颜色 的需求,改进下代码。

增加了测量辅助线、对齐辅助线、生成svg图等,基本满足需求。

demo中包括了生成json、svg字符串和下载svg图。

<script src="../plugins/fabric.min.js"></script>
<script src="../plugins/aligning_guidelines.js"></script>
<link href="../plugins/bootstrap-5.1.3/css/bootstrap.min.css" rel="stylesheet" />
<script src="../plugins/jquery/jquery-3.3.1.js"></script>
<script src="../plugins/bootstrap-5.1.3/js/bootstrap.bundle.min.js"></script>

<style>
  #tooltip {
    position: absolute;
    display: none;
    background-color: white;
    border: 1px solid silver;
    box-shadow: 0 0 5px grey;
    border-radius: 3px;
  }
  #tooltip div {
    display: inline-block;
    padding: 0.25rem 0.5rem;
  }
  #tooltip div span:last-child {
    font-weight: bold;
    margin-left: 2rem;
  }
  #genSvg {
    border: 1px solid silver;
  }
</style>
<body>
  <div class="row g-2">
    <div class="col-2">
      <ul class="nav nav-tabs mt-2" id="myTab">
        <li class="nav-item">
          <a class="nav-link active" data-bs-toggle="tab" data-bs-target="#shape-rect" style="cursor: pointer">矩形</a>
        </li>
        <li class="nav-item">
          <a class="nav-link" data-bs-toggle="tab" data-bs-target="#shape-ellipse" style="cursor: pointer">椭圆形</a>
        </li>
      </ul>
      <div class="tab-content">
        <div class="tab-pane fade active show" id="shape-rect" role="tabpanel">
          <div class="row g-2">
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">宽度</span>
                <input type="number" min="0" class="form-control" />
              </div>
            </div>
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">高度</span>
                <input type="number" min="0" class="form-control" />
              </div>
            </div>
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">名字</span>
                <input type="text" class="form-control" />
              </div>
            </div>
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">数量</span>
                <input type="number" min="1" class="form-control" />
              </div>
            </div>
          </div>
          <button class="btn btn-secondary mt-2" onclick="addRect()">新增</button>
        </div>
        <div class="tab-pane fade" id="shape-ellipse" role="tabpanel">
          <div class="row g-2">
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">外水平半径</span>
                <input type="number" min="1" class="form-control" />
              </div>
            </div>
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">外垂直半径</span>
                <input type="number" min="1" class="form-control" />
              </div>
            </div>
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">内水平半径</span>
                <input type="number" min="1" class="form-control" />
              </div>
            </div>
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">内垂直半径</span>
                <input type="number" min="1" class="form-control" />
              </div>
            </div>
            <div class="col-12">
              <div class="input-group">
                <span class="input-group-text">名字</span>
                <input type="text" class="form-control" />
              </div>
            </div>
          </div>
          <button class="btn btn-secondary mt-2" onclick="addEllipse()">新增</button>
        </div>
      </div>

      <hr class="my-1" />
      <div class="input-group mb-2">
        <span class="input-group-text">名字</span>
        <input type="text" id="object_name" class="form-control" />
      </div>
      <button type="button" class="btn btn-secondary" onclick="setObjectName()">修改名称</button>

      <hr class="my-1" />
      <button class="btn btn-secondary mb-2" onclick="cloneShape()">复制</button>
      <button class="btn btn-secondary mb-2" onclick="delShape()">删除</button>
      <button class="btn btn-secondary mb-2" onclick="getAllShape()">预览</button>

      <hr class="my-1" />
      <div>
        水平距离:<span id="distince_x"></span>
        <br />
        垂直距离:<span id="distince_y"></span>
      </div>
      <div>共<span id="info">0</span>个</div>
    </div>
    <div class="col-10" style="overflow: auto">
      <canvas id="example" style="border: solid 1px #ccc"></canvas>
      <canvas id="example_re" style="border: solid 1px #ccc"></canvas>
      <div id="genSvg"></div>
    </div>
  </div>

  <div id="tooltip">
    <div>
      <span>Name</span>
      <span></span>
    </div>
  </div>
  <script>
    const img_path = "../images/2.jpg";
    const canvas = new fabric.Canvas("example");
    canvas.setBackgroundImage(img_path, canvas.renderAll.bind(canvas)); //背景图
    initAligningGuidelines(canvas); //用于对齐的辅助线

    const canvas_re = new fabric.Canvas("example_re");
    // const canvas_re = new fabric.StaticCanvas("example_re");//无交互
    canvas_re.selection = false;
    const tooltip = document.getElementById("tooltip");
    let canvas_params, canvas_re_params;
    let gbl_coord_x = 0;
    let gbl_coord_y = 0;

    const bg_img = new Image();
    bg_img.src = img_path;
    bg_img.onload = function () {
      //将canvas的宽高设置为背景图片的宽高
      canvas.setWidth(bg_img.width);
      canvas.setHeight(bg_img.height);
      canvas_params = document.querySelector("#example").getBoundingClientRect(); //元素的位置宽高等信息

      //辅助线
      const line_h = new fabric.Line([0, 3, canvas.getWidth(), 3], {
        stroke: "silver",
        strokeWidth: 2,
        excludeFromExport: true, //不导出
        strokeDashArray: [5, 5], //虚线
        lockRotation: true,
        lockScalingFlip: true,
        lockSkewingX: true,
        lockSkewingY: true,
        lockScalingX: true,
        lockScalingY: true,
      });
      line_h.set("name", "auxiliary_line_h"); //自定义属性
      const line_v = new fabric.Line([3, 0, 3, canvas.getHeight()], {
        stroke: "silver",
        strokeWidth: 2,
        excludeFromExport: true,
        strokeDashArray: [5, 5],
        lockRotation: true,
        lockScalingFlip: true,
        lockSkewingX: true,
        lockSkewingY: true,
        lockScalingX: true,
        lockScalingY: true,
      });
      line_v.set("name", "auxiliary_line_v"); //自定义属性
      canvas.add(line_h);
      canvas.add(line_v);

      canvas_re.setWidth(bg_img.width);
      canvas_re.setHeight(bg_img.height);
      canvas_re_params = document.querySelector("#example_re").getBoundingClientRect();
    };

    //通用属性
    const default_prop = {
      transparentCorners: false, //选中时 控制手柄的样式
      borderColor: "green",
      cornerColor: "green",
      cornerSize: 5,
      lockRotation: true, //禁止旋转
      lockScalingFlip: true, //禁止缩放时翻转
      lockSkewingX: true, //禁止水平方向扭曲
      lockSkewingY: true, //禁止垂直方向扭曲
    };
    const default_vals_rect = [20, 50, "rect", 1];
    const default_vals_ellipse = [75, 75, 50, 50, "ellipse"];
    $("#shape-rect input").each((idx, elem) => {
      elem.value = default_vals_rect[idx];
    });
    $("#shape-ellipse input").each((idx, elem) => {
      elem.value = default_vals_ellipse[idx];
    });

    //根据参数值或者表单值添加矩形区域
    function addRect() {
      const total_width = canvas.getWidth();
      const total_height = canvas.getHeight();

      const vals = [];
      let flag = true;
      const elems = $("#shape-rect input");
      for (let i = 0; i < elems.length - 1; i++) {
        const val = elems[i].value.trim();
        if (val.length < 1 || (i != 2 && isNaN(val)) || parseInt(val) < 0) {
          flag = false;
          alert("各参数不能为空,第1、2、4项值应为正整数");
          break;
        } else if (vals[0] > total_width || vals[0] + vals[2] >= total_width || vals[1] > total_height || vals[1] + vals[3] >= total_height) {
          flag = false;
          alert("请检查参数值");
          break;
        }
        vals.push(i < 2 ? parseInt(val) : val);
      }

      let num = $("#shape-rect input:eq(3)").val().trim();
      num = isNaN(num) ? 1 : parseInt(num);
      const c_width = vals[0] + 10;
      const c_height = vals[1] + 10;
      const col_num_max = Math.floor(total_width / c_width);
      const row_num_max = Math.floor(total_height / c_height);
      let count = 0;
      for (let i = 0; i < row_num_max; i++) {
        for (let j = 0; j < col_num_max; j++) {
          count++;
          if (count <= num) {
            const left = 5 + j * c_width;
            const top = 5 + i * c_height;
            const rect = new fabric.Rect(
              Object.assign(
                {
                  left: left,
                  top: top,
                  originX: "left",
                  originY: "top",
                  width: vals[0],
                  height: vals[1],
                  fill: "rgba(255, 0, 0, 0.5)",
                },
                default_prop
              )
            );
            rect.set("name", vals[2]); //自定义属性
            canvas.add(rect);

            rewriteToSvg(rect);
          }
        }
      }
    }

    //根据参数值或者表单值添加椭圆形区域
    function addEllipse() {
      let flag = true;
      const vals = [];
      const elems = $("#shape-ellipse input");
      for (let i = 0; i < elems.length; i++) {
        const val = elems[i].value.trim();
        if (val.length < 1 || (i != 4 && isNaN(val)) || parseInt(val) < 0) {
          flag = false;
          alert("各参数不能为空,前4项值应为正整数");
          break;
        }
        vals.push(i < 4 ? parseInt(val) : val);
      }

      if (vals[2] >= vals[0] || vals[3] >= vals[1]) {
        vals[2] = 0;
        vals[3] = 0;
      }

      if (vals[2] > 1 && vals[3] > 1) {
        //模拟圆环-导出svg显示圆环
        const ellipse = new fabric.Ellipse({
          left: 0,
          top: 0,
          rx: vals[0],
          ry: vals[1],
          fill: "rgba(255, 0, 0, 0.5)",
        });

        const ellipse_inner = new fabric.Ellipse({
          rx: vals[2],
          ry: vals[3],
          left: vals[0] - vals[2],
          top: vals[1] - vals[3],
          fill: "rgba(255, 255, 255, 0.9)",
        });

        const group = new fabric.Group(
          [ellipse, ellipse_inner],
          Object.assign(
            {
              left: 0,
              top: 0,
            },
            default_prop
          )
        );

        ellipse.set("name", vals[4]); //自定义属性
        canvas.add(group);

        rewriteToSvg(ellipse);
      } else {
        const ellipse = new fabric.Ellipse(
          Object.assign(
            {
              left: 0,
              top: 0,
              rx: vals[0],
              ry: vals[1],
              fill: "rgba(255, 0, 0, 0.5)",
            },
            default_prop
          )
        );
        ellipse.set("name", vals[4]);
        canvas.add(ellipse);

        rewriteToSvg(ellipse);
      }
    }

    //将名字的变化 同步到canvas上
    function setObjectName() {
      const active_obj = canvas.getActiveObject();
      if (typeof active_obj == "undefined" || active_obj == null) return;

      if (active_obj.type != "activeSelection" && active_obj instanceof fabric.Object) {
        if (active_obj.type != "group") {
          active_obj.set("name", $("#object_name").val());
        } else {
          //圆环由两个圆组成,只设置外圆
          const objects = active_obj.getObjects();
          objects[0].set("name", $("#object_name").val());
        }
        canvas.renderAll();
      }
      $("#object_name").val("");
    }

    //复制选中的对象
    function cloneShape() {
      if (typeof canvas.getActiveObject() === "undefined") return;

      let pos_x = 0;
      let pos_y = 0;
      //选择多个对象时需要重新计算坐标位置
      if (canvas.getActiveObject().type == "activeSelection") {
        const activeSelection = canvas.getActiveObject();
        pos_x = (activeSelection.left + (activeSelection.left + activeSelection.width)) / 2;
        pos_y = (activeSelection.top + (activeSelection.top + activeSelection.height)) / 2;
      }

      const active_objs = canvas.getActiveObjects();
      for (let i in active_objs) {
        const active_obj = active_objs[i];
        if (active_obj instanceof fabric.Object) {
          const left = pos_x + active_obj.get("left") + 20;
          const top = pos_y + active_obj.get("top") + 20;

          if (active_obj.type == "group") {
            active_obj.clone((clone) => {
              const objects = active_obj.getObjects();
              const objects_clone = clone.getObjects();
              objects_clone[0].set("name", objects[0].get("name"));
              clone.set("left", left);
              clone.set("top", top);
              clone.set(default_prop);
              canvas.add(clone);
              rewriteToSvg(clone);
            });
          } else {
            active_obj.clone((clone) => {
              clone.set("name", active_obj.get("name"));
              clone.set("left", left);
              clone.set("top", top);
              clone.set(default_prop);
              canvas.add(clone);
              rewriteToSvg(clone);
            });
          }
        }
      }
    }

    //重写toSVG方法,使生成的图形带上自定义属性name
    function rewriteToSvg(obj) {
      obj.toSVG = (function (toSVG) {
        return function () {
          const svgString = toSVG.call(this);
          const domParser = new DOMParser();
          const doc = domParser.parseFromString(svgString, "image/svg+xml");
          let type = this.type;
          if (this.type == "group") {
            const objects = this.getObjects();
            type = objects[0].type;
            const parentG = doc.querySelector(`${type}`);
            parentG.setAttribute("name", objects[0].name);
          } else {
            const parentG = doc.querySelector(`${type}`);
            parentG.setAttribute("name", this.name);
          }
          return doc.documentElement.outerHTML;
        };
      })(obj.toSVG);
    }

    //删除选中的对象
    function delShape() {
      const active_objs = canvas.getActiveObjects();
      for (let i in active_objs) {
        const active_obj = active_objs[i];
        if (active_obj instanceof fabric.Object) {
          canvas.remove(active_obj);
        }
      }
    }

    //获取canvas上的所有对象数据,在另外的canvas上重绘预览,导出svg格式
    function getAllShape() {
      canvas.discardActiveObject();

      const data = [];
      const all_obj = canvas.getObjects(); //获取canvas上所有对象

      const prop = {
        selectable: false,
        lockMovementX: true,
        lockMovementY: true,
        lockRotation: true,
        lockScalingFlip: true,
        lockSkewingX: true,
        lockSkewingY: true,
        lockScalingX: true,
        lockScalingY: true,
        hasControls: false,
      };

      const data_json = canvas.toJSON(["name"]); //转换时包含自定义属性Id
      canvas_re.loadFromJSON(data_json, canvas_re.renderAll.bind(canvas_re), function (o, obj) {
        obj.set(prop);
        obj.set({ fill: getCorlor(obj.get("name")) });
      });

      console.log(canvas_re.getObjects()); //结果为空,有点奇怪

      document.querySelector("#info").innerHTML = data_json["objects"].length;

      const svgString = canvas.toSVG();
      $("#genSvg").html(svgString);

      // 下载 SVG 文件
      const file = new Blob([svgString], { type: "image/svg+xml" });
      const url = URL.createObjectURL(file);
      const link = document.createElement("a");
      link.href = url;
      link.download = "test.svg";
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      URL.revokeObjectURL(url);
    }

    //根据自定义属性name的值填入颜色
    function getCorlor(name) {
      if (name == "11") {
        return "rgba(0, 0, 255, 0.5)";
      } else if (name == "12") {
        return "rgba(0, 255, 0, 0.5)";
      } else {
        //渐变色
        const gradient = new fabric.Gradient({
          type: "linear",
          gradientUnits: "percentage",
          coords: { x1: 0, y1: 0, x2: 1, y2: 0 },
          colorStops: [
            { offset: 0, color: "red" },
            { offset: 1, color: "blue" },
          ],
        });
        return gradient;
      }
    }

    //tooltip
    canvas.on("mouse:over", function (opt) {
      if (opt.target && opt.target.type !== "activeSelection" && opt.target["name"] != "auxiliary_line_h" && opt.target["name"] != "auxiliary_line_v") {
        let name;
        if (opt.target.type == "group") {
          const objects = opt.target.getObjects();
          name = objects[0].get("name");
        } else {
          name = opt.target.get("name");
        }
        $("#tooltip div span:last-child").text(name);
        const values = opt.target.getBoundingRect();
        tooltip.style.display = "initial";
        tooltip.style.top = canvas_params["top"] + values["top"] + Math.floor(values["height"] / 2) + 5 + "px";
        tooltip.style.left = canvas_params["left"] + values["left"] + Math.floor(values["width"] / 2) + 5 + "px";
      }
    });
    canvas.on("mouse:out", function (opt) {
      tooltip.style.display = "none";
    });

    canvas_re.on("mouse:over", function (opt) {
      if (opt.target && opt.target.type !== "activeSelection") {
        let name;
        if (opt.target.type == "group") {
          const objects = opt.target.getObjects();
          name = objects[0].get("name");
        } else {
          name = opt.target.get("name");
        }
        $("#tooltip div span:last-child").text(name);
        const values = opt.target.getBoundingRect();
        tooltip.style.display = "initial";
        tooltip.style.top = canvas_re_params["top"] + values["top"] + Math.floor(values["height"] / 2) + 5 + "px";
        tooltip.style.left = canvas_re_params["left"] + values["left"] + Math.floor(values["width"] / 2) + 5 + "px";
      }
    });
    canvas_re.on("mouse:out", function (opt) {
      tooltip.style.display = "none";
    });

    //选中事件
    canvas.on("selection:created", function (opt) {
      const active_obj = canvas.getActiveObject();
      if (active_obj.type != "activeSelection") {
        if (active_obj.type == "group") {
          const objects = active_obj.getObjects();
          $("#object_name").val(objects[0].get("name"));
        } else {
          $("#object_name").val(active_obj.get("name"));
        }
      } else {
        $("#object_name").val("");
      }
    });
    //选中事件
    canvas.on("selection:updated", function (opt) {
      const active_obj = canvas.getActiveObject();
      if (active_obj.type != "activeSelection") {
        if (active_obj.type == "group") {
          const objects = active_obj.getObjects();
          $("#object_name").val(objects[0].get("name"));
        } else {
          $("#object_name").val(active_obj.get("name"));
        }
      } else {
        $("#object_name").val("");
      }
    });
    //取消选中事件
    canvas.on("selection:cleared", function (opt) {
      $("#object_name").val("");
    });

    //测量图片上两点的水平距离和垂直距离
    canvas.on("mouse:down", function (opt) {
      $("#distince_x").text(Math.abs(opt.pointer.x - gbl_coord_x).toFixed(1));
      $("#distince_y").text(Math.abs(opt.pointer.y - gbl_coord_y).toFixed(1));
      gbl_coord_x = opt.pointer.x;
      gbl_coord_y = opt.pointer.y;
    });

    // 监听键盘事件,主要用于对齐位置
    document.addEventListener("keydown", function (event) {
      const active_objs = canvas.getActiveObjects();

      for (let i in active_objs) {
        const active_obj = active_objs[i];
        if (active_obj instanceof fabric.Object) {
          switch (event.keyCode) {
            case 37: // 左键
              active_obj.set({ left: active_obj.get("left") - 1 });
              break;
            case 38: // 上键
              active_obj.set({ top: active_obj.get("top") - 1 });
              break;
            case 39: // 右键
              active_obj.set({ left: active_obj.get("left") + 1 });
              break;
            case 40: // 下键
              active_obj.set({ top: active_obj.get("top") + 1 });
              break;
            default:
              break;
          }
        }
      }
      canvas.renderAll();
    });
  </script>
</body>

下图中图1为canvas画布,图2为json导出的json重画,图3为导出svg字符串放到html中

绿线为fabric提供的辅助线组件,移动图形对齐时显示,非常好用。不方便截图

 

实现矩形区域比较简单,实现环形区域过程中比较费劲,最终采用了2个不同直径的同心圆形,记录下踩的坑。

<script src="../plugins/fabric.min.js"></script>
<div>
  <canvas id="example" style="border: solid 1px #ccc"></canvas>
  <div id="genSvg"></div>
</div>
<script>
  const canvas = new fabric.Canvas("example");
  canvas.setWidth(600);
  canvas.setHeight(100);

  {
    const circle = new fabric.Circle({
      top: 0,
      left: 0,
      radius: 30,
      fill: "red",
    });
    canvas.add(circle);
  }

  {
    const circle = new fabric.Circle({
      top: 0,
      left: 60,
      radius: 30,
      fill: "red",
    });
    const clipPath = new fabric.Circle({
      radius: 10,
      top: -10,
      left: -10,
    });
    circle.clipPath = clipPath;
    canvas.add(circle);
  }

  {
    const circle = new fabric.Circle({
      top: 0,
      left: 120,
      radius: 30,
      fill: "red",
    });
    const clipPath = new fabric.Circle({
      radius: 10,
      top: -10,
      left: -10,
    });
    clipPath.inverted = true;
    circle.clipPath = clipPath;
    canvas.add(circle);
  }

  {
    const x = 210;
    const y = 30;
    const r1 = 30;
    const r2 = 10;
    const point1_x = r1 + x;
    const point2_x = point1_x - 2 * r1;
    const point3_x = r2 + x;
    const point4_x = point3_x - 2 * r2;
    const path = new fabric.Path(
      `M${point1_x},${y} A${r1},${r1} 0 0,1 ${point2_x},${y} 
                A${r1},${r1} 0 0,1 ${point1_x},${y} 
                M${point3_x},${y} A${r2},${r2} 0 0,1 ${point4_x},${y} 
                A${r2},${r2} 0 0,1 ${point3_x},${y}`,
      {
        stroke: "red",
        fill: "transparent",
        hasControls: false,
      }
    );
    canvas.add(path);
  }

  {
    const x = 270;
    const y = 30;
    const r1 = 30;
    const r2 = 10;
    const point1_x = r1 + x;
    const point2_x = point1_x - 2 * r1;
    const point3_x = r2 + x;
    const point4_x = point3_x - 2 * r2;
    const path = new fabric.Path(
      `M${point1_x},${y} A${r1},${r1} 0 0,1 ${point2_x},${y} 
                A${r1},${r1} 0 0,1 ${point1_x},${y} 
                M${point3_x},${y} A${r2},${r2} 0 0,1 ${point4_x},${y} 
                A${r2},${r2} 0 0,1 ${point3_x},${y}`,
      {
        fill: "red",
        hasControls: false,
      }
    );
    canvas.add(path);
  }

  {
    const circle = new fabric.Circle({
      top: 0,
      left: 300,
      radius: 25,
      stroke: "red",
      strokeWidth: 10,
      fill: "transparent",
    });
    canvas.add(circle);
  }

  document.querySelector("#genSvg").innerHTML = canvas.toSVG();
</script>

上面一行为canvas画布,下面一行为导出的svg字符串放到html中,圆1为填充圆形,圆2、3为采用clipPath实现,圆4、5为采用path实现,,圆4、5为采用path实现stroke实现

1 使用 clipPath实现,导出的svg图和显示不一样

2 使用path实现:导出svg图片和显示一样,缺点:fill属性设置后即为填充圆形,而不是填充圆环

3 使用stroke实现 :圆半径为内径和外径的均值,strokeWidth为外径和内径的差值,导出svg图片和显示一样,然echarts的map似乎不支持stroke

以上3中实现圆环区域的方法利用导出json重画均无问题,只是生成svg图存在各种坑。

 

标签:canvas,const,fabric,svg,js,vals,active,obj,name
From: https://www.cnblogs.com/caroline2016/p/18101701

相关文章

  • SVG描边 - CSS3实现动画绘制矢量图
    使用SVG的stroke-dasharray及stroke-dashoffset,结合CSS3animation实现画笔绘制矢量图的动画效果,如下:html<svgxmlns="http://www.w3.org/2000/svg"pointer-events="none"class="leaflet-zoom-animated"width="1452"heigh......
  • 在Vue.js框架中,activated和created的区别
    在Vue.js框架中,activated和created两个钩子函数都是在组件被创建时执行的函数,但它们的使用场景略有不同。createdcreated钩子函数是在组件被创建(即实例化)时执行的,可以用它来初始化组件的数据、监听事件、调用方法等操作。它是组件生命周期中的第一个钩子函数,通常用于组件初......
  • JS中为什么forEach方法不能终止
    forEach是我们在日常工作中经常使用到的方法,但是你有什么尝试使用forEach进行停止或终止等操作呢?一、走进forEach对于forEach了解不多的,只知道它可以遍历数组,如果有这么一个操作:一个数组[0,1,2,3,4,5],打印出[0,1,2,3],可能会这么写。1.1尝试returnconstarr=[0,......
  • svg中viewbox图解分析
     svg中有一个viewbox属性,中文翻译为视区,就是在svg上截取一小块,放大到整个svg显示。这个属性初学的话有点难以理解,这边做一个简单的示例,一看就明白了。1)先来一个svg,宽高各位300,设置一下边框:<svgwidth="300"height="300"style="border:1pxsolidsteelblue"></svg>......
  • js 回调 callback
    <html><head><title>按钮点击事件示例</title><script>functiona(callback){console.log("执行函数a!");console.log("调用回调函数");callback();//调用回调函数}functionb(){console.log("回调函数b"......
  • Day52:WEB攻防-XSS跨站&反射型&存储型&DOM型&标签闭合&输入输出&JS代码解析
    目录XSS跨站-输入输出-原理&分类&闭合XSS跨站-分类测试-反射&存储&DOM反射型XSS存储型XSSDOM-base型XSS:(某案例测试)知识点:1、XSS跨站-输入输出-原理&分类&闭合2、XSS跨站-分类测试-反射&存储&DOMXSS跨站-输入输出-原理&分类&闭合漏洞原理:接受输入数据,输出显......
  • Day53:WEB攻防-XSS跨站&SVG&PDF&Flash&MXSS&UXSS&配合上传&文件添加脚本
    目录MXSSUXSS:UniversalCross-SiteScriptingHTML&SVG&PDF&SWF-XSS&上传&反编译(有几率碰到)SVG-XSSPDF-XSSPython生成XSSFlash-XSS知识点:1、XSS跨站-MXSS&UXSS2、XSS跨站-SVG制作&配合上传3、XSS跨站-PDF制作&配合上传4、XSS跨站-SWF制作&反编译&上传XSS......
  • nodejs 核心模块
    nodejs核心模块Node.js的核心模块是Node.js发行版自带的模块,无需通过npm进行安装。你可以在Node.js的官方文档中找到所有核心模块的详细列表。以下是一些常用核心模块的简单使用示例:http 模块:创建一个简单的HTTP服务器。  consthttp=requir......
  • 将markmap生成的svg脑图转为png并下载
    将markmap生成的svg脑图转为png并下载markmap是一个非常方便的库,可以直接将md格式转为思维导图。在开发过程中我发现,目前没有将markmap生成的脑图转为png格式保存的代码或是插件。以下是我给出的解决方案这是一段svg标签,用来生成markmap的容器,具体生成的代码不多赘述。<divc......
  • 华为OD机试 - 最多购买宝石数目(Java & JS & Python & C & C++)
    须知哈喽,本题库完全免费,收费是为了防止被爬,大家订阅专栏后可以私信联系退款。感谢支持文章目录须知题目描述输入描述输出描述解题思路:题目描述橱窗里有一排宝石,不同的宝石对应不同的价格,宝石的价格标记为gems[i]0≤i<nn=gems.length宝石可同时......