jquery+bootstrap实现DOM转图片并下载

news2024/11/14 17:57:26

🍊jquery实现DOM结构转图片并下载

版本介绍:

  • Bootstrap v3.3.7
  • jQuery v3.5.1
  • domToImage.js
    根据Bootstrap实现dialog上一步下一步多个弹窗交互进行大肆修改,完善了第二步生成图片的功能与更强的交互
1.、功能说明
  1. 重新设置bootstrap主题色
  • 包括按钮:默认(default)、成功(success)、错误(danger)、主要(primary)
  • 提示:成功(success)、错误(danger)
  1. 内容区以card形式展示,纯js实现分页功能
  2. 共两步骤:
  • 第一步选择模板,
  • 第二步自定义编辑信息,预览模板信息
  • 点击按钮将DOM生成png图片(或者gif图片),并下载
  • 预览DOM生成的模板:会由于符号等问题出现占不满的情况,会再次进行填充,重新渲染panel
2. 效果图
  1. 效果图一:
    n0nw5-viseo.gif
  2. 效果图二:
    广告22.gif
3. 代码
  1. index.html
<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
  <link rel="stylesheet" href="./css/bootstrap3.3.7.css">
  <link rel="stylesheet" href="./css/theme.css">
  <link rel="stylesheet" href="./css/index.css">
  <script src="./js/jquery3.5.1.js"></script>
  <script src="./js/bootstrap3.3.7.js"></script>
  <script src="./js/domToImage.js"></script>
</head>

<body>
  <!-- Button trigger modal -->
  <button type="button" class="btn btn-primary btn-lg openModule">
    在线生成
  </button>

  <!-- 选择模板 -->
  <div class="modal fade in" id="module" tabindex="-1" role="dialog" aria-labelledby="moduleLabel">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
              aria-hidden="true">&times;</span></button>
          <h4 class="modal-title" id="moduleLabel">生成广告</h4>
        </div>
        <div class="modal-body">
          <!-- 模板列表 -->
          <div class="module-list"></div>
          <!-- 分页 -->
          <nav aria-label="Page navigation" class="pagination-box">
            <ul class="pagination"></ul>
          </nav>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-primary next">下一步</button>
        </div>
      </div>
    </div>
  </div>

  <!-- 生成广告 -->
  <div class="modal fade in" id="advertising" tabindex="-1" role="dialog" aria-labelledby="advertisingLabel">
    <div class="modal-dialog" role="document">
      <div class="modal-content">
        <div class="modal-header">
          <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span
              aria-hidden="true">&times;</span></button>
          <h4 class="modal-title" id="advertisingLabel">生成广告</h4>
        </div>
        <div class="modal-body">
          <!-- 输入表单 -->
          <form class="form-horizontal">
            <div class="form-group company_name">
              <label for="inputEmail3" class="col-sm-3 control-label">公司名称:</label>
              <div class="col-sm-7">
                <input class="form-control" id="company_name" maxlength="100" placeholder="请输入公司名称">
                <span class="company_name_verification help-block">请输入公司名称</span>
              </div>
            </div>
            <div class="form-group">
              <label for="inputPassword3" class="col-sm-3 control-label">联系人:</label>
              <div class="col-sm-7">
                <input class="form-control" id="contact_name" maxlength="20" placeholder="请输入联系人">
              </div>
            </div>
            <div class="form-group">
              <label for="inputPassword3" class="col-sm-3 control-label">联系电话:</label>
              <div class="col-sm-7">
                <input class="form-control" id="contact_phone" maxlength="50" placeholder="请输入联系电话">
              </div>
            </div>
            <div class="form-group">
              <label for="inputPassword3" class="col-sm-3 control-label">广告语:</label>
              <div class="col-sm-7">
                <textarea class="form-control" id="ad_words" rows="3" maxlength="500" placeholder="请输入广告语"
                  style="resize: none;"></textarea>
              </div>
            </div>
          </form>
          <!-- 预览图 -->
          <div class="image-container"></div>
        </div>
        <div class="modal-footer">
          <button type="button" class="btn btn-default last">上一步</button>
          <button type="button" class="btn btn-success confirmGIF">生成*.gif</button>
          <button type="button" class="btn btn-primary confirmJPG">生成*.jpg</button>
        </div>
      </div>
    </div>
  </div>

  <script src="./js/index.js"></script>

</body>

</html>
  1. index.js
// TODO
const info = {
  company_name: "测试服务有限公司",
  contact_name: "耿先生",
  contact_phone: "1513006500 195**1155",
  ad_words:
    "这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告这是测试广告广告",
};

/**
 * @function 获取模板列表数据
 * @variable listParams 请求列表分页参数
 * @variable total 总条数
 * @variable maxPage 最大分页数
 */

let listParams = {
  pageSize: 10,
  pageNumber: 1,
};
let total = 0;
let maxPage = 0;
function getList() {
  $.ajax({
    type: "POST",
    url: " https://mock.mengxuegu.com/mock/66a0c71a904326081fb3bdf9/module_list",
    data: listParams,
  }).done(function (res) {
    if (res.code === 200) {
      console.log(res.result);
      total = res.result.total;
      renderModuleList(res.result.items);
      renderActiveModule(res.result.items);
      renderPagination();
      renderActivePagination();
    }
  });
}

/**
 * @function 渲染模板列表
 */
function renderModuleList(data) {
  // 渲染前先清空
  $(".module-list").empty();
  let ctx = "";
  data.map((item) => {
    ctx +=
      '<div class="module-item" data-kid=' +
      item.id +
      "><div>模板名称:" +
      item.temp_title +
      "</div><div>尺寸:" +
      item.width +
      "x" +
      item.height +
      '</div><div class="module-image"><img src="' +
      item.img_url +
      '" class="img-responsive" alt="' +
      item.temp_title +
      '"></div></div>';
  });

  $(".module-list").append($(ctx));
}

/**
 * @function 选择模板
 */
let selectedModuleId = null;
$(".module-list").on("click", ".module-item", function () {
  selectedModuleId = $(this).data("kid");
  $(this)
    .addClass("module-item-active")
    .siblings()
    .removeClass("module-item-active");
});

/**
 * @function 渲染选中模板
 */
function renderActiveModule() {
  if (!selectedModuleId) {
    return;
  }

  $(".module-item").map((item, v) => {
    if ($(v).data("kid") === selectedModuleId) {
      $($(".module-item")[item]).addClass("module-item-active");
    }
  });
}

/**
 * @function 渲染分页
 */
function renderPagination() {
  // 渲染前先清空
  $(".pagination").empty();
  maxPage = Math.ceil(total / 10);
  // 1. 上一页
  let page = `<li data-prop="prev">
        <a href="#" aria-label="Previous">
          <span aria-hidden="true">&laquo;</span>
        </a>
      </li>`;
  // 2. 页码
  for (let i = 1; i <= maxPage; i++) {
    page += '<li><a href="#" data-prop="' + i + '">' + i + "</a></li>";
  }
  // 3. 下一页
  page += `<li data-prop="next">
            <a href="#" aria-label="Next">
              <span aria-hidden="true">&raquo;</span>
            </a>
          </li>`;

  $(".pagination").append($(page));
}

/**
 * @function 渲染高亮分页
 */
function renderActivePagination() {
  $($(".pagination li")[listParams.pageNumber])
    .addClass("active")
    .siblings()
    .removeClass("active");
}

/**
 * @function 点击分页
 */
$(".pagination").on("click", "li", function () {
  const prop = $(this).data("prop");
  if (prop === "prev") {
    // 上一页
    if (listParams.pageNumber > 1) {
      listParams.pageNumber--;
    }
  } else if (prop === "next") {
    // 下一页
    if (listParams.pageNumber < maxPage) {
      listParams.pageNumber++;
    }
  } else {
    // 页码
    const page = $(this).text();
    listParams.pageNumber = page * 1;
  }
  getList();
});

/**
 * @function 设置提示内容
 * @param { string } message 提示消息
 * @param { 'success' | 'danger' } type 提示类型
 */
function setMessage(message, type = "success") {
  const len = $(".text-message").length;
  if (len) {
    return;
  }
  $("body").append(`<div class="text-message">` + message + `</div>`);
  setTimeout(() => {
    $(".text-message")
      .eq(0)
      .addClass("text-" + type);
    $(".text-message").eq(0).css({
      top: "45px",
    });
  }, 200);

  setTimeout(() => {
    $(".text-message").eq(0).css({
      top: "-45px",
    });
    setTimeout(() => {
      $(".text-message").eq(0).remove();
    }, 200);
  }, 3000);
}

/**
 * @function 重置数据
 */
function reset() {
  $(".module-list").empty();
  $(".pagination").empty();
  listParams = {
    pageSize: 10,
    pageNumber: 1,
  };
  total = 0;
  maxPage = 0;
}

/**
 * @function 点击在线生成按钮
 */
$(".openModule").on("click", function () {
  $("#module").modal("show");
});

/**
 * @function 打开模板列表弹窗
 */
$("#module").on("show.bs.modal", function () {
  if ($("#advertising").css("display") === "block") {
    return;
  }
  getList();
});

/**
 * @function 关闭模板列表弹窗
 */
$("#module").on("hidden.bs.modal", function () {
  // 如果是第二步回到第一步,不重置数据
  if ($("#advertising").css("display") === "block") {
    return;
  }
  reset();
});

/**
 * @function 点击下一步
 */
$(".next").click(function () {
  if (!selectedModuleId) {
    setMessage("请选择模板", "danger");
    return;
  }
  $("#module").modal("hide");
  $("#advertising").modal("show");
});

/**
 * @function 点击上一步
 */
$(".last").click(function () {
  $("#advertising").modal("hide");
  $("#module").modal("show");
});

/**
 * @function 打开生成广告弹窗
 */
$("#advertising").on("show.bs.modal", function () {
  $(".image-container").empty();
  const { company_name, contact_name, contact_phone, ad_words } = info;
  $("#company_name").val(company_name);
  $("#contact_name").val(contact_name);
  $("#contact_phone").val(contact_phone);
  $("#ad_words").val(ad_words);
  getModuleDetail();
});

/**
 * @function 关闭生成广告弹窗
 */
$("#advertising").on("hidden.bs.modal", function () {
  $(".company_name").removeClass("has-error");
  $(".company_name_verification").hide();
  // 如果是第二步回到第一步,不重置数据
  if ($("#module").css("display") === "block") {
    return;
  }
  reset();
});

/**
 * form表单input改变事件
 */
$("#company_name").on("input", function () {
  companyNameVerify();
  setCompanyNameVal();
});
$("#contact_name").on("input", function () {
  setContactVal();
});
$("#contact_phone").on("input", function () {
  setContactVal();
});
$("#ad_words").on("input", function () {
  const { is_two_page } = moduleDetail;
  if (is_two_page) {
    descStatus = "init";
    calcPanelNum();
  } else {
    setDescVal();
  }
});

/**
 * 设置预览DOM显示值
 */
function setCompanyNameVal() {
  $(".text-company").text(
    $("#company_name").val().trim()
      ? $("#company_name").val()
      : "请输入公司名称"
  );
}
function setContactVal() {
  const contact_name = $("#contact_name").val().trim();
  const contact_phone = $("#contact_phone").val().trim();
  const contact_name_phone = contact_name + contact_phone;
  $(".text-contact").text(contact_name_phone || "请输入联系信息");
}
function setDescVal() {
  $(".text-desc").text(
    $("#ad_words").val() ? $("#ad_words").val() : "请输入广告语"
  );
}

/**
 * @function 获取选中模板详情
 * @variable 模板详情
 */
let moduleDetail = null;
function getModuleDetail() {
  $.ajax({
    type: "POST",
    url: "https://mock.mengxuegu.com/mock/66a0c71a904326081fb3bdf9/module_detail/",
    data: { id: selectedModuleId },
  }).done(function (res) {
    if (res.code === 200) {
      moduleDetail = res.data;
      drawingDomElements();
    }
  });
}

/**
 * @function 绘制dom元素
 */
function drawingDomElements() {
  // 判断广告语是不是展示在第2页
  const { is_two_page: descNewPage } = moduleDetail;
  if (descNewPage) {
    drawingMultiPanel();
  } else {
    drawingImageContainer();
  }
}

/**
 * @function 绘制公司标题元素
 */
function drawingImageContainer() {
  const { is_two_page: descNewPage } = moduleDetail;

  $(".image-container")
    .prepend(` <div class="image-panel image-1" id="DomToImage1">
            <div class="content">
              <div class="text-company"></div>
              <div class="text-contact"></div>
              <div class="text-desc"></div>
            </div>
          </div>`);
  drawingImagePanel();
  drawingMaskName();
  drawingContentName("content");
  drawingCompany();
  drawingContact();
  if (descNewPage) {
    $(".text-desc").remove();
  } else {
    drawingDesc();
  }
}
/**
 * @function 绘制image-panel元素
 */
function drawingImagePanel() {
  const {
    width: basicWidth,
    height: basicHeight,
    is_backcolor_or_img: basicBackground, // 1 color 2 img
    background_color: basicBackgroundColor,
    back_img_url: basicBackgroundImg,
  } = moduleDetail;
  const style = {
    width: basicWidth + "px",
    height: basicHeight + "px",
  };

  if (basicBackground === 1) {
    // 判断是否是渐变色
    const basicBackgroundColorType = basicBackgroundColor.includes(",")
      ? "gradient"
      : "single";
    if (basicBackgroundColorType === "single") {
      style.background = basicBackgroundColor;
    } else {
      const colorList = basicBackgroundColor.split(",");
      style.background = `linear-gradient(135deg, ${colorList[0]}, ${colorList[1]})`;
    }
  } else {
    style.backgroundImage = `url(${basicBackgroundImg})`;
    style.backgroundRepeat = "no-repeat";
    style.backgroundSize = "cover";
  }
  $(".image-panel").css(style);
}

/**
 * @function 绘制mask元素
 * @variable basicBackgroundMaskCheck 蒙版
 * @variable basicBackgroundMaskColor 蒙版颜色
 * @variable basicBackgroundMaskOpacityCheck 是否透明
 * @variable basicBackgroundMaskOpacity 透明度
 */
function drawingMaskName() {
  const { back_mask, mask_tm_val, is_backcolor_or_img } = moduleDetail;
  const style = {};

  basicBackgroundMaskCheck = back_mask === "" ? false : true;
  basicBackgroundMaskColor = back_mask === "" ? "#000000" : back_mask;

  if (basicBackgroundMaskCheck) {
    style.background = basicBackgroundMaskColor;
  }

  basicBackgroundMaskOpacityCheck = mask_tm_val === 101 ? false : true;
  basicBackgroundMaskOpacity = mask_tm_val === 101 ? 50 : mask_tm_val;

  if (basicBackgroundMaskOpacityCheck) {
    style.opacity = basicBackgroundMaskOpacity + "%";
  }

  if (is_backcolor_or_img === 2 && basicBackgroundMaskCheck) {
    $(".image-panel").prepend('<div class="mask"></div>');
    $(".mask").css(style);
  } else {
    $(".mask").remove();
  }
}

/**
 * @function 绘制content元素
 */
function drawingContentName(name) {
  const { up_down_margin, left_right_margin } = moduleDetail;
  const style = {};
  style.paddingTop = style.paddingBottom = (up_down_margin || 20) + "px";
  style.paddingLeft = style.paddingRight = (left_right_margin || 20) + "px";
  $("." + name).css(style);
}

/**
 * @function 绘制company元素
 */
function drawingCompany() {
  const {
    company_font: companyFontFamily,
    company_font_size: companyFontSize,
    company_font_color: companyFontColor,
    is_company_bold: companyFontBold,
    is_company_xt: companyFontItalic,
  } = moduleDetail;
  const style = {
    fontFamily: companyFontFamily,
    fontSize: companyFontSize + "px",
    color: companyFontColor,
  };
  if (companyFontBold) {
    style.fontWeight = "bold";
  }
  if (companyFontItalic) {
    style.fontStyle = "italic";
  }
  $(".text-company").css(style);
  setCompanyNameVal();
}

/**
 * @function 绘制contact元素
 */
function drawingContact() {
  const {
    contact_font: contactFontFamily,
    contact_font_size: contactFontSize,
    contact_font_color: contactFontColor,
    contact_margin: contactMarginTop,
    is_contact_bold: contactFontBold,
    is_contact_xt: contactFontItalic,
  } = moduleDetail;
  const style = {
    fontFamily: contactFontFamily,
    fontSize: contactFontSize + "px",
    color: contactFontColor,
    marginTop: contactMarginTop + "px",
  };
  if (contactFontBold) {
    style.fontWeight = "bold";
  }
  if (contactFontItalic) {
    style.fontStyle = "italic";
  }

  $(".text-contact").css(style);
  setContactVal();
}

/**
 * @function 绘制desc元素
 */
function drawingDesc() {
  const {
    is_two_page: descNewPage,
    ad_font: descFontFamily,
    ad_font_size: descFontSize,
    ad_font_color: descFontColor,
    is_ad_bold: descFontBold,
    is_ad_xt: descFontItalic,
    ad_top_margin: descMarginTop,
    line_height: descLineHeight,
  } = moduleDetail;
  const style = {
    fontFamily: descFontFamily,
    fontSize: descFontSize + "px",
    color: descFontColor,
    lineHeight: descLineHeight,
  };
  if (descFontBold) {
    style.fontWeight = "bold";
  }
  if (descFontItalic) {
    style.fontStyle = "italic";
  }
  if (!descNewPage) {
    style.marginTop = descMarginTop + "px";
    setDescVal();
  }

  $(".text-desc").css(style);
}

/**
 * @function 广告语展示在第2页,绘制panel
 * @variable descList 广告语panel列表基础文字
 * @variable panelFontBaseCount panel内字体基础数量
 * @variable descCopy 复制广告内容,填充处理
 */
let descList = [];
let panelFontBaseCount = 0;
let descCopy = "";

function drawingMultiPanel() {
  const {
    width: basicWidth,
    height: basicHeight,
    ad_font_size: descFontSize,
    line_height: descLineHeight,
    up_down_margin,
    left_right_margin,
  } = moduleDetail;

  const containerW = basicWidth - (left_right_margin || 20) * 2;
  const containerH = basicHeight - (up_down_margin || 20) * 2;

  const lineHeight = descFontSize * descLineHeight;
  const lineCount = parseInt(containerH / lineHeight);
  const preLineCount = parseInt(containerW / descFontSize);
  panelFontBaseCount = preLineCount * lineCount;
  calcPanelNum();
}

/**
 * @function 计算panel数量, 给descList赋初始值,panelFontBaseCount基础值,会由于符号等问题出现占不满的情况,需要再次进行填充,重新渲染panel
 */
function calcPanelNum() {
  currentIndex = 0;
  descStatus = "init";
  descList = [];
  descCopy = $("#ad_words").val();
  const ad_words = $("#ad_words").val();
  const pages = Math.ceil(ad_words.length / panelFontBaseCount);

  for (let i = 0; i < pages; i++) {
    const start = i * panelFontBaseCount;
    const end = start + panelFontBaseCount;
    const page = ad_words.substring(start, end);
    descList.push(page);
  }
  drawingOtherPanel();
}

/**
 * @function 广告语展示在第2页,绘制panel
 */
function drawingOtherPanel() {
  $(".image-container").empty();
  drawingImageContainer();
  descList.map((item, i) => {
    $(".image-container").append(
      `<div class="image-panel" id="DomToImage2` +
        i +
        `"><div class="content-padding"></div></div>`
    );

    $(".content-padding")
      .eq(i)
      .append(
        `<div class="content" id="descContent` +
          i +
          `"><div class="text-desc">` +
          item +
          `</div></div>`
      );
    drawingImagePanel();
    drawingMaskName();
    drawingContentName("content-padding");
    drawingDesc();
  });
  setTimeout(() => {
    if (descStatus === "init") {
      isOverFlow(panelFontBaseCount);
    }
  }, 200);
}

/**
 * @function 判断是否超出容器
 */
let descStatus = "init";
let currentIndex = 0;
function isOverFlow(textCount) {
  if (!$("#descContent" + currentIndex).length) {
    return;
  }

  let scrollHeight = $("#descContent" + currentIndex)[0].scrollHeight;
  let offsetHeight = $("#descContent" + currentIndex)[0].offsetHeight;

  // 初始进入肯定会等于或者小于
  if (scrollHeight <= offsetHeight) {
    // 填充一个字
    const end = textCount + 1;
    descList[currentIndex] = descCopy.substring(0, end);

    $("#descContent" + currentIndex + " .text-desc").text(
      descList[currentIndex]
    );
    if (descList[currentIndex].length < descCopy.length) {
      if (currentIndex < $(".content-padding").length - 1) {
        isOverFlow(textCount + 1);
      }
    } else {
      descStatus = "end";
      descList.splice(currentIndex + 1);
      drawingOtherPanel();
    }
  } else {
    // 超出减1,还原到最后填充之前
    const exceedCount = textCount - 1;
    // 截取, 修改descCopy
    descCopy = descCopy.substring(exceedCount);
    descList[currentIndex] = descList[currentIndex].substring(0, textCount - 1);

    $("#descContent" + currentIndex + " .text-desc").text(
      descList[currentIndex]
    );
    currentIndex++;
    isOverFlow(panelFontBaseCount);
  }
}

/**
 * @function 点击生成*.jpg按钮
 */
$(".confirmJPG").click(function (e) {
  companyNameVerify();
  getToJPG();
});

/**
 * @function 公司名称校验
 */
function companyNameVerify() {
  if (!$("#company_name").val().trim()) {
    $(".company_name").addClass("has-error");
    $(".company_name_verification").fadeIn(200);
  } else {
    $(".company_name").removeClass("has-error");
    $(".company_name_verification").fadeOut(50);
  }
}

/**
 * @function 生成.jpg图片
 */
function getToJPG() {
  const { is_two_page } = moduleDetail;

  drawingDomToImage("DomToImage1", function (dataUrl) {
    setMessage("生成成功");
    downloadFileByBase64(dataUrl, $("#company_name").val().trim());
  });

  // 广告展示多张图
  if (is_two_page && $("#ad_words").val()) {
    const len = $(".image-container").children().length;
    for (let i = 0; i < len - 1; i++) {
      drawingDomToImage("DomToImage2" + i, function (dataUrl) {
        setMessage("生成成功");
        downloadFileByBase64(dataUrl, $("#company_name").val().trim() + i);
      });
    }
  }
}

/**
 * @function 绘制方法
 * @param {string} 绘制DOM id
 * @param {function} 回调函数
 */
function drawingDomToImage(id, cb) {
  let node = document.getElementById(id);
  domtoimage
    .toPng(node)
    .then(function (dataUrl) {
      cb(dataUrl);
    })
    .catch(function () {
      setMessage("生成失败", "danger");
    });
}

function downloadFileByBase64(base64, name) {
  var myBlob = dataURLtoBlob(base64);
  var myUrl = URL.createObjectURL(myBlob); //创建图片的临时url
  downloadFile(myUrl, name);
}

/**
 * @function url转blob
 */
function dataURLtoBlob(dataUrl) {
  var arr = dataUrl.split(","),
    mime = arr[0].match(/:(.*?);/)[1],
    bstr = atob(arr[1]),
    n = bstr.length,
    u8arr = new Uint8Array(n);
  while (n--) {
    u8arr[n] = bstr.charCodeAt(n);
  }
  return new Blob([u8arr], { type: mime });
}

/**
 * @function 下载图片
 */
function downloadFile(url, name) {
  var a = document.createElement("a"); //新建一个a链接
  a.setAttribute("href", url); // a链接的url为图片的url
  a.setAttribute("download", name);
  a.setAttribute("target", "_blank");
  let clickEvent = document.createEvent("MouseEvents");
  clickEvent.initEvent("click", true, true);
  a.dispatchEvent(clickEvent);
}

/**
 * @function 点击生成*.gif按钮
 */
$(".confirmGIF").click(function () {
  const { is_two_page } = moduleDetail;
  if (is_two_page && $("#ad_words").val()) {
    companyNameVerify();
    getToGIF();
  } else {
    setMessage("必须两张图片以上才可生成gif", "danger");
  }
});

/**
 * @function 生成.gif图片
 */
function getToGIF() {
  const len = $(".image-container").children().length;
  for (let i = 0; i < len; i++) {
    if (i) {
      drawingDomToImage("DomToImage2" + (i - 1), function (dataUrl) {
        handleCreateImageDone({ url: dataUrl, index: i });
      });
    } else {
      drawingDomToImage("DomToImage1", function (dataUrl) {
        handleCreateImageDone({ url: dataUrl, index: i });
      });
    }
  }
}

let uploadImgList = [];
function handleCreateImageDone({ url, index }) {
  uploadImgList.push({ url, index });
  jpgCreateGif();
}

// 上传jpg生成gif
function jpgCreateGif() {
  const files = uploadImgList
    .sort((a, b) => a.index - b.index)
    .map((item, index) => {
      return base64ToFile(item.url, index);
    });

  const len = $(".image-container").children().length;
  const filesLen = files.length;
  if (filesLen === len) {
    apiCreateGif(jsToFormData({ files }));
  }
}

function base64ToFile(base64Data, fileName) {
  const blob = dataURLtoBlob(base64Data);
  return new File([blob], fileName);
}

/**
 * @function 对象转formdata格式
 */
function jsToFormData(obj) {
  let formData = new FormData();
  for (var key in obj) {
    if (Array.isArray(obj[key])) {
      obj[key].map((item) => {
        formData.append(key, item);
      });
    } else {
      formData.append(key, obj[key]);
    }
  }
  return formData;
}

/**
 * @function 广告生成gif接口返回
 */
function apiCreateGif(formData) {
  $.ajax({
    type: "POST",
    url: "https://mock.mengxuegu.com/mock/66a0c71a904326081fb3bdf9/add_gif/",
    data: formData,
    processData: false,
    contentType: false,
    xhrFields: {
      responseType: "blob",
    },
  }).done(function (res) {
    // 返回gif数据流,需要后台接口实现
    return;
    downloadGifFile(res, $("#company_name").val().trim());
    uploadImgList = [];
  });
}

function downloadGifFile(res, name) {
  const url = window.URL.createObjectURL(
    new Blob([res], { type: "image/gif" })
  );
  let a = document.createElement("a");
  a.download = name + ".gif"; // 文件名
  a.href = url;
  document.body.appendChild(a);
  a.click();
  document.body.removeChild(a);
  window.URL.revokeObjectURL(url);
}

  • 方法说明:

    1. 计算panel数量calcPanelNum:
    • 当获取容器的实际高度与可视高度时,由于无渲染,所以获取不到,需要计算出基准字数进行渲染,然后计算填充
    • 获取容器的实际高度与可视高度,当超出,则说明不需要填充
    let scrollHeight = $("#descContent" + currentIndex)[0].scrollHeight;
    let offsetHeight = $("#descContent" + currentIndex)[0].offsetHeight;
    
    1. 判断是否超出容器:isOverFlow: 若在渲染完panel后不添加此方法,会出现占位不满的情况
      image.png
  1. index.css
html, body {
  line-height: 1.15!important;
}

.modal{
  overflow-x: hidden;
  overflow-y: auto;
}
.modal-content{
  width: 80vh;
  min-width: 750px;
}

/* 模板列表*/
.module-list{
  display: flex;
  flex-wrap: wrap;
}

.module-item {
  box-sizing: border-box;
  width: calc(50% - 20px);
  display: flex;
  flex-direction: column;
  padding: 10px;
  margin: 10px;
  border: 1px solid #e5e5e5;
  border-radius: 10px;
  cursor: pointer;
}

.module-item:hover {
  border-color: #c6e2ff;
  background-color: #ecf5ff;
}

.module-item:active,.module-item:focus {
  border-color: #409eff;
  background-color: #ecf5ff;
}

.module-item-active,.module-item-active:hover{
  border-color: #409eff;
  background-color: #ecf5ff;
}

.module-image{
  flex: 1;
  display: flex;
  justify-content: flex-end;
  align-items: end;
  margin-top: 5px;
} 
.module-image img {
  max-width: 100px;
  max-height: 100px;
}

/* 分页容器 */
.pagination-box{
  display: flex;
  justify-content: center;
}

/*
  生成广告
*/
.company_name_verification{
  display: none;
}

/* 模板DOM/预览图 */
.image-container{
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  padding: 20px;
  overflow-x: auto;
}
.image-panel{
  width: 708px;
  height: 316px;
  background: #000000;
  border-radius: 4px;
  margin-bottom: 30px;
  box-shadow: 0 0 10px rgba(0, 0, 0, 0.2);
  overflow: hidden;
  position: relative;
}

.mask {
  width: 100%;
  height: 100%;
  position: absolute;
  left: 0;
  top: 0;
}

.content-padding {
  width: 100%;
  height: 100%;
}

.content {
  width: 100%;
  height: 100%;
  position: relative;
  z-index: 1;
}

.image-1 .content {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
}

/* 公司名称 */
.text-company {
  font-size: 30px;
  color: green;
  text-align: center;
}

/* 联系信息 */
.text-contact {
  font-size: 20px;
  color: #ffffff;
  text-align: center;
  margin-top: 50px;
  white-space: pre-wrap;
}

/* 广告语 */
.text-desc {
  width: 100%;
  height: 100%;
  font-size: 16px;
  color: #ffffff;
  white-space: pre-wrap;
  text-align: justify;
}
  1. 修改主题theme.css
/* 默认按钮 */
.btn-default{
  border-color: #dcdfe6;
  background-color: #ffffff;
}

.btn-default:hover{
  border-color: #c6e2ff;
  background-color: #ecf5ff;
}

.btn-default:active{
  color: #409eff;
  border-color: #409eff;
  background-color: #ecf5ff;
  outline:none;
}

.btn-default:focus{
  border-color: #dcdfe6!important;
  background-color: #ffffff!important;
  outline:none!important;
}

/* 主要按钮 */
.btn-primary{
  border-color: #409EFF;
  background-color: #409EFF;
}

.btn-primary:hover{
  border-color: #79bbff;
  background-color: #79bbff;
}

.btn-primary:active{
  border-color: #337ecc;
  background-color: #337ecc;
  outline:none;
}

.btn-primary:focus{
  border-color: #409EFF!important;
  background-color: #409EFF!important;
  outline:none!important;
}

/* 成功按钮 */
.btn-success{
  border-color: #67c23a;
  background-color: #67c23a;
}

.btn-success:hover{
  border-color: #95d475;
  background-color: #95d475;
}

.btn-success:active{
  border-color: #529b2e;
  background-color: #529b2e;
  outline:none;
}

.btn-success:focus{
  border-color: #67c23a!important;
  background-color: #67c23a!important;
  outline:none!important;
}

/* error */

.has-error .control-label,.has-error .help-block{
  color: #f56c6c;
}

.has-error .form-control,
.has-error .form-control:focus {
  border-color: #f56c6c;
}

/* 提示信息 */
.text-message {
  position: fixed;
  top: 0;
  left: 50%;
  z-index: 2000;
  transform: translate(-50%, 0);
  width: 500px;
  padding: 8px 16px;
  border-radius: 4px;
  text-align: center;
  opacity: 0;
  transition: opacity 0.2s, top .2s;
}

.text-danger{
  background-color: #fef0f0;
  border: 1px solid #f56c6c;
  color: #f56c6c;
  opacity: 1;
}

.text-success{
  background-color: #f0f9eb;
  border: 1px solid #67c23a;
  color: #67c23a;
  opacity: 1;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1950837.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

JAVA.抽象、接口、内部类

1.抽象 共性&#xff0c;父类定义抽象方法&#xff0c;子类必须重写&#xff0c;或者子类也是抽象类 示例代码 animal package animalabstract;//定义抽象类animal public abstract class animal {String name;int age;//定义抽象方法eat&#xff0c;子类必须重写public abs…

《企业实战分享 · CodeGeeX 初体验》

&#x1f4e2; 大家好&#xff0c;我是 【战神刘玉栋】&#xff0c;有10多年的研发经验&#xff0c;致力于前后端技术栈的知识沉淀和传播。 &#x1f497; &#x1f33b; CSDN入驻不久&#xff0c;希望大家多多支持&#xff0c;后续会继续提升文章质量&#xff0c;绝不滥竽充数…

企业级视频拍摄与编辑SDK的全面解决方案

视频已成为企业传播信息、展示品牌、连接用户的重要桥梁&#xff0c;如何高效、专业地制作高质量视频内容&#xff0c;成为众多企业面临的共同挑战。美摄科技&#xff0c;作为视音频技术领域的创新先锋&#xff0c;以其强大的视频拍摄与编辑SDK&#xff0c;为企业量身打造了一站…

react中简单的配置路由

1.安装react-router-dom npm install react-router-dom 2.新建文件 src下新建page文件夹&#xff0c;该文件夹下新建login和index文件夹用于存放登录页面和首页&#xff0c;再在对应文件夹下分别新建入口文件index.js&#xff1b; src下新建router文件用于存放路由配置文件…

【Android】Activity生命周期与五种启动模式

文章目录 生命周期返回栈Activity状态生命周期方法 启动模式standard模式singleTask模式singleTop模式singleInstance模式singleInstancePerTask模式配置方式 生命周期 返回栈 每个Activity的状态由它在Activity栈&#xff08;又叫“回退栈back stack”&#xff09;中的位置决…

Web Worker 详细介绍

Web Worker 详细介绍 如果我们有一些处理密集型的任务&#xff0c;但是不想让它们在主线程上运行&#xff08;那样会使浏览器/UI变慢&#xff09;&#xff0c;这时候我们可能会希望 JavaScript 可以以多线程的方式操作。 虽然 JavaScript 是单线程了&#xff0c;但是在浏览器…

超声波眼镜清洗机哪款好用又实惠?4款高评分眼镜清洗机机型深度测评

眼镜党都知道超声波清洗机吧&#xff0c;每次眼镜脏了&#xff0c;去眼镜店清洗&#xff0c;店员用的就是超声波清洗机。利用超声波的原理&#xff0c;这种清洗机可以深入物品内部进行清洁&#xff0c;效果非常出色。相比手工清洗&#xff0c;超声波清洗机能在清洁过程中保护镜…

远程项目调试-informer2020

informer2020 Informer: Beyond Efficient Transformer for Long Sequence Time-Series Forecasting(原文&#xff09;Informer 是一个基于Transformer的模型&#xff0c;是为了应对长依赖关系而开发的。本文的主要主题是序列预测。序列预测可以在任何具有不断变化的数据的地方…

做短视频素材哪里找?去哪里下载?自媒体下载素材网站分享

自媒体视频创作&#xff1a;高质量素材网站大公开&#xff01; 大家好&#xff0c;我是一名热情的短视频创作者。今天&#xff0c;我要与大家分享一些寻找优质视频素材的秘诀。无论是新手还是老手&#xff0c;这些建议都能帮助你的视频在众多平台中脱颖而出&#xff0c;吸引更…

系统移植(四)u-boot移植 ② basic版本

文章目录 一、u-boot移植&#xff08;一&#xff09;生成u-boot源码1. 配置交叉编译器&#xff0c;修改u-boot源码目录下的MAKEFILE文件4. 执行make <board_name>_defconfig命令&#xff0c;配置u-boot源码5. make menuconfig---图形化界面配置6. 根据DK1板子的设备树文件…

ISP 代理提供商:互联网安全的关键参与者

简介&#xff1a;互联网安全的演变态势 互联网改变了我们互动、工作和开展业务的方式&#xff0c;但也带来了与安全性和可访问性相关的重大挑战。在这个数字时代&#xff0c;互联网服务提供商 (ISP) 代理提供商在解决这些问题方面发挥着关键作用。他们提供的基本服务不仅可以增…

PDF解锁网站

https://smallpdf.com/cn/unlock-pdfhttps://smallpdf.com/cn/unlock-pdfhttps://www.freemypdf.comhttps://www.freemypdf.com

LVGL使用上一个不方便的问题记录

slider这个控件&#xff0c;应该画在蓝色框的里面&#xff0c;源码实现将中心画在蓝色框上&#xff0c;导致总会有两边超出的情况出现&#xff0c;真想修改源码&#xff0c;作者不知道咋想的&#xff1f;&#xff1f;&#xff1f;

vue3 Router 点击index中的按钮,查看相应的详情信息,并且传递id,及其路由的定义方法。

1、路由的定义 结构如下: 2、路由定义代码&#xff1a; {path: tabs,name: TabsDemo,component: () > import(/views/demo/feat/tabs/index.vue),meta: {title: t(routes.demo.feat.tabs),hideChildrenInMenu: true,},children: [{path: detail/:id,name: TabDetail,compon…

封装和桥接Unity 协程体系

简介 协程&#xff08;Coroutine&#xff09;在C#中是一种特殊的函数&#xff0c;它允许开发者编写可以暂停执行并在未来某个时刻恢复执行的代码块。协程通常用于实现异步操作&#xff0c;如延时执行、等待某个事件发生、或者分段执行复杂的任务。在Unity游戏引擎中&#xff0c…

Cuda编程模型中常见的错误检测方法

Cuda编程模型中常见的错误检测方法 1 CUDA错误检测简介2 直接嵌入检测函数2.1 检测函数介绍2.2 使用示例 3 封装在.cuh头文件中嵌入3.1 创建 error.cuh 头文件3.2 在 CUDA 程序中包含 error.cuh 并调用 CHECK 宏3.3 使用示例 1 CUDA错误检测简介 CUDA编程模型中的错误检测是确…

【C++】选择结构案例-三只小猪称体重

案例问题 假设有三只小猪A、B、C&#xff0c;在输入三者体重后希望能输出他们各自的体重并测出谁最重 思路 先让A与B相比较&#xff0c;如果A重&#xff0c;则让A和C相比较&#xff0c;如果A重则输出A最重&#xff0c;否则输出C最重 在最开始的条件&#xff08;AB相比较&am…

JQuery简单实现ul li点击菜单项被选中的菜单项保持高亮状态(导航ul li点击切换样式)

效果&#xff1a; JS&#xff1a; $(function () {//遍历list&#xff08;一般为ul li&#xff09;$("#menu a").each(function () {//给当前项添加点击事件&#xff08;点击后切换样式&#xff09;$(this).bind(click,function () {// 移除其他所有项的active类$(&…

Sokit(TCP/UDP调试工具)

下载&#xff1a;http://www.winwin7.com/soft/56522.html#xiazai Sokit中文版是一款免费开源的TCP / UDP 测试&#xff08;调试&#xff09;工具&#xff0c;它主要可以用于接收和发送TCP/UDP数据包&#xff0c;让你更深的了解网络状况&#xff0c;能够有效地接收、发送、转…

Linux中的进程1

进程的概念 程序&#xff1a;二进制文件 进程&#xff1a;启动的程序 所有的数据都在内存中 需要占据更多的系统资源 cpu&#xff0c;物理内存&#xff08;RAM&#xff09; 并行和并发 并发&#xff1a;在操作系统中&#xff0c;是指一个时间段中有几个程序都处于已启动…