精华内容
下载资源
问答
  • web代码截屏
    2022-06-24 14:31:42

    1、截屏,截取整个页面

    get_screenshot_as_file(img_path)
    

    2、截屏,截取单个元素

    screenshot(img_path)
    

    3、案例

    import time
    from selenium import webdriver
    
    # 创建浏览器驱动对象
    driver = webdriver.Chrome(r"D:\chromedriver\chromedriver_win32-V103.0.5060.53\chromedriver.exe")
    # 访问网址
    driver.get("http://www.baidu.com")
    
    # 截屏,截取整个页面
    time.sleep(3)
    driver.get_screenshot_as_file("./all.png")
    
    # 截屏,截取单个元素
    time.sleep(3)
    driver.find_element_by_id("kw").screenshot("./ele.png")
    
    更多相关内容
  • 内容索引:Delphi源码,图形处理,Capture.rar Delphi 全屏抓图、常规截屏,区域截图,活动窗口抓图代码,支持滚屏截图功能。截屏完毕,可打印,可保存为指定格式,可显示图片信息,代码可在delphi7中直接编译成功。源...
  • Vue网络屏幕截图· web端自定义截屏插件(Vue3版),运行视频:插件,本插件仅支持Vue3,如需在其他平台使用请移步: 效果图如下:插件安装yarn add vue-web-screen-shot# ornpm install vue-web-screen-shot --save...
  • import形式使用插件在需要使用截屏插件的业务代码中导入插件import ScreenShort from "js-web-screen-shot";在业务代码中使用时实例化插件即可new ScreenShort();:warning:注意:实例化插件时一定要等dom加载完成,...
  • 实现Web端自定义截屏

    千次阅读 2021-02-02 14:11:39
    那么,我们就需要为我们的产品实现一个自定义截屏的功能,用户点完"截图"按钮后,框选任意区域,随后在框选的区域内进行圈选、画箭头、马赛克、直线、打字等操作,做完操作后用户可以选择保存框选区域的内容到本地...

    前言

    当客户在使用我们的产品过程中,遇到问题需要向我们反馈时,如果用纯文字的形式描述,我们很难懂客户的意思,要是能配上问题截图,这样我们就能很清楚的知道客户的问题了。

    那么,我们就需要为我们的产品实现一个自定义截屏的功能,用户点完"截图"按钮后,框选任意区域,随后在框选的区域内进行圈选、画箭头、马赛克、直线、打字等操作,做完操作后用户可以选择保存框选区域的内容到本地或者直接发送给我们。

    聪明的开发者可能已经猜到了,这是QQ/微信的截图功能,我的开源项目正好做到了截图功能,在做之前我找了很多资料,没有发现web端有这种东西存在,于是我就决定参照QQ的截图自己实现一个并做成插件供大家使用。

    本文就跟大家分享下我在做这个"自定义截屏功能"时的实现思路以及过程,欢迎各位感兴趣的开发者阅读本文。

    运行结果视频:实现web端自定义截屏

    写在前面

    本文插件的写法采用的是Vue3的compositionAPI,如果对其不了解的开发者请移步我的另一篇文章:使用Vue3的CompositionAPI来优化代码量

    实现思路

    我们先来看下QQ的截屏流程,进而分析它是怎么实现的。

    截屏流程分析

    我们先来分析下,截屏时的具体流程。

    • 点击截屏按钮后,我们会发现页面上所有动态效果都静止不动了,如下所示。

      iShot2021-02-01 14.05.04
    • 随后,我们按住鼠标左键进行拖动,屏幕上会出现黑色蒙板,鼠标的拖动区域会出现镂空效果,如下所示。

      image-20210201141538157
    • 完成拖拽后,框选区域的下方会出现工具栏,里面有框选、圈选、箭头、直线、画笔等工具,如下图所示。

      image-20210201142541572

    • 点击工具栏中任意一个图标,会出现画笔选择区域,在这里可以选择画笔大小、颜色如下所示。

      image-20210201143108803
    • 随后,我们在框选的区域内进行拖拽就会绘制出对应的图形,如下所示。

      image-20210201144004992

    • 最后,点击截图工具栏的下载图标即可将图片保存至本地,或者点击对号图片会自动粘贴到聊天输入框,如下所示。

      image-20210201144450745

    截屏实现思路

    通过上述截屏流程,我们便得到了下述实现思路:

    • 获取当前可视区域的内容,将其存储起来
    • 为整个cnavas画布绘制蒙层
    • 在获取到的内容中进行拖拽,绘制镂空选区
    • 选择截图工具栏的工具,选择画笔大小等信息
    • 在选区内拖拽绘制对应的图形
    • 将选区内的内容转换为图片

    实现过程

    我们分析出了实现思路,接下来我们将上述思路逐一进行实现。

    获取当前可视区域内容

    当点击截图按钮后,我们需要获取整个可视区域的内容,后续所有的操作都是在获取的内容上进行的,在web端我们可以使用canvas来实现这些操作。

    那么,我们就需要先将body区域的内容转换为canvas,如果要从零开始实现这个转换,有点复杂而且工作量很大。

    还好在前端社区种有个开源库叫html2canvas可以实现将指定dom转换为canvas,我们就采用这个库来实现我们的转换。

    接下来,我们来看下具体实现过程:

    新建一个名为screen-short.vue的文件,用于承载我们的整个截图组件。

    • 首先我们需要一个canvas容器来显示转换后的可视区域内容
    <template>
      <teleport to="body">
        <!--截图区域-->
        <canvas
          id="screenShotContainer"
          :width="screenShortWidth"
          :height="screenShortHeight"
          ref="screenShortController"
        ></canvas>
      </teleport>
    </template>
    

    此处只展示了部分代码,完整代码请移步:screen-short.vue

    • 在组件挂载时,调用html2canvas提供的方法,将body中的内容转换为canvas,存储起来。
    import html2canvas from "html2canvas";
    import InitData from "@/module/main-entrance/InitData";
    
    export default class EventMonitoring {
      // 当前实例的响应式data数据
      private readonly data: InitData;
      // 截图区域canvas容器
      private screenShortController: Ref<HTMLCanvasElement | null>;
      // 截图图片存放容器
      private screenShortImageController: HTMLCanvasElement | undefined;
      
      constructor(props: Record<string, any>, context: SetupContext<any>) {
        // 实例化响应式data
        this.data = new InitData();
        // 获取截图区域canvas容器
        this.screenShortController = this.data.getScreenShortController();
        
        onMounted(() => {
          // 设置截图区域canvas宽高
          this.data.setScreenShortInfo(window.innerWidth, window.innerHeight);
          
          html2canvas(document.body, {}).then(canvas => {
            // 装载截图的dom为null则退出
            if (this.screenShortController.value == null) return;
            
            // 存放html2canvas截取的内容
            this.screenShortImageController = canvas;
          })
        })
      }
    }
    

    此处只展示了部分代码,完整代码请移步:EventMonitoring.ts

    为canvas画布绘制蒙层

    我们拿到了转换后的dom后,我们就需要绘制一个透明度为0.6的黑色蒙层,告知用户你现在处于截屏区域选区状态。

    具体实现过程如下:

    • 创建DrawMasking.ts文件,蒙层的绘制逻辑在此文件中实现,代码如下。
    /**
     * 绘制蒙层
     * @param context 需要进行绘制canvas
     */
    export function drawMasking(context: CanvasRenderingContext2D) {
      // 清除画布
      context.clearRect(0, 0, window.innerWidth, window.innerHeight);
      // 绘制蒙层
      context.save();
      context.fillStyle = "rgba(0, 0, 0, .6)";
      context.fillRect(0, 0, window.innerWidth, window.innerHeight);
      // 绘制结束
      context.restore();
    }
    
    

    ⚠️注释已经写的很详细了,对上述API不懂的开发者请移步:clearRectsavefillStylefillRectrestore

    • html2canvas函数回调中调用绘制蒙层函数
    html2canvas(document.body, {}).then(canvas => {
      // 获取截图区域画canvas容器画布
      const context = this.screenShortController.value?.getContext("2d");
      if (context == null) return;
      // 绘制蒙层
      drawMasking(context);
    })
    

    绘制镂空选区

    我们在黑色蒙层中拖拽时,需要获取鼠标按下时的起始点坐标以及鼠标移动时的坐标,根据起始点坐标和移动时的坐标,我们就可以得到一个区域,此时我们将这块区域的蒙层凿开,将获取到的canvas图片内容绘制到蒙层下方,这样我们就实现了镂空选区效果。

    整理下上述话语,思路如下:

    • 监听鼠标按下、移动、抬起事件
    • 获取鼠标按下、移动时的坐标
    • 根据获取到的坐标凿开蒙层
    • 将获取到的canvas图片内容绘制到蒙层下方
    • 实现镂空选区的拖拽与缩放

    实现的效果如下:

    111

    具体代码如下:

    export default class EventMonitoring {
       // 当前实例的响应式data数据
      private readonly data: InitData;
      
      // 截图区域canvas容器
      private screenShortController: Ref<HTMLCanvasElement | null>;
      // 截图图片存放容器
      private screenShortImageController: HTMLCanvasElement | undefined;
      // 截图区域画布
      private screenShortCanvas: CanvasRenderingContext2D | undefined;
      
      // 图形位置参数
      private drawGraphPosition: positionInfoType = {
        startX: 0,
        startY: 0,
        width: 0,
        height: 0
      };
      // 临时图形位置参数
      private tempGraphPosition: positionInfoType = {
        startX: 0,
        startY: 0,
        width: 0,
        height: 0
      };
    
      // 裁剪框边框节点坐标事件
      private cutOutBoxBorderArr: Array<cutOutBoxBorder> = [];
      
      // 裁剪框顶点边框直径大小
      private borderSize = 10;
      // 当前操作的边框节点
      private borderOption: number | null = null;
      
      // 点击裁剪框时的鼠标坐标
      private movePosition: movePositionType = {
        moveStartX: 0,
        moveStartY: 0
      };
    
      // 裁剪框修剪状态
      private draggingTrim = false;
      // 裁剪框拖拽状态
      private dragging = false;
      // 鼠标点击状态
      private clickFlag = false;
      
      constructor(props: Record<string, any>, context: SetupContext<any>) {
         // 实例化响应式data
        this.data = new InitData();
        
        // 获取截图区域canvas容器
        this.screenShortController = this.data.getScreenShortController();
        
        onMounted(() => {
          // 设置截图区域canvas宽高
          this.data.setScreenShortInfo(window.innerWidth, window.innerHeight);
          
          html2canvas(document.body, {}).then(canvas => {
            // 装载截图的dom为null则退出
            if (this.screenShortController.value == null) return;
    
            // 存放html2canvas截取的内容
            this.screenShortImageController = canvas;
            // 获取截图区域画canvas容器画布
            const context = this.screenShortController.value?.getContext("2d");
            if (context == null) return;
    
            // 赋值截图区域canvas画布
            this.screenShortCanvas = context;
            // 绘制蒙层
            drawMasking(context);
    
            // 添加监听
            this.screenShortController.value?.addEventListener(
              "mousedown",
              this.mouseDownEvent
            );
            this.screenShortController.value?.addEventListener(
              "mousemove",
              this.mouseMoveEvent
            );
            this.screenShortController.value?.addEventListener(
              "mouseup",
              this.mouseUpEvent
            );
          })
        })
      }
      // 鼠标按下事件
      private mouseDownEvent = (event: MouseEvent) => {
        this.dragging = true;
        this.clickFlag = true;
        
        const mouseX = nonNegativeData(event.offsetX);
        const mouseY = nonNegativeData(event.offsetY);
        
        // 如果操作的是裁剪框
        if (this.borderOption) {
          // 设置为拖动状态
          this.draggingTrim = true;
          // 记录移动时的起始点坐标
          this.movePosition.moveStartX = mouseX;
          this.movePosition.moveStartY = mouseY;
        } else {
          // 绘制裁剪框,记录当前鼠标开始坐标
          this.drawGraphPosition.startX = mouseX;
          this.drawGraphPosition.startY = mouseY;
        }
      }
      
      // 鼠标移动事件
      private mouseMoveEvent = (event: MouseEvent) => {
        this.clickFlag = false;
        
        // 获取裁剪框位置信息
        const { startX, startY, width, height } = this.drawGraphPosition;
        // 获取当前鼠标坐标
        const currentX = nonNegativeData(event.offsetX);
        const currentY = nonNegativeData(event.offsetY);
        // 裁剪框临时宽高
        const tempWidth = currentX - startX;
        const tempHeight = currentY - startY;
        
        // 执行裁剪框操作函数
        this.operatingCutOutBox(
          currentX,
          currentY,
          startX,
          startY,
          width,
          height,
          this.screenShortCanvas
        );
        // 如果鼠标未点击或者当前操作的是裁剪框都return
        if (!this.dragging || this.draggingTrim) return;
        // 绘制裁剪框
        this.tempGraphPosition = drawCutOutBox(
          startX,
          startY,
          tempWidth,
          tempHeight,
          this.screenShortCanvas,
          this.borderSize,
          this.screenShortController.value as HTMLCanvasElement,
          this.screenShortImageController as HTMLCanvasElement
        ) as drawCutOutBoxReturnType;
      }
      
        // 鼠标抬起事件
      private mouseUpEvent = () => {
        // 绘制结束
        this.dragging = false;
        this.draggingTrim = false;
        
        // 保存绘制后的图形位置信息
        this.drawGraphPosition = this.tempGraphPosition;
        
        // 如果工具栏未点击则保存裁剪框位置
        if (!this.data.getToolClickStatus().value) {
          const { startX, startY, width, height } = this.drawGraphPosition;
          this.data.setCutOutBoxPosition(startX, startY, width, height);
        }
        // 保存边框节点信息
        this.cutOutBoxBorderArr = saveBorderArrInfo(
          this.borderSize,
          this.drawGraphPosition
        );
      }
    }
    

    ⚠️绘制镂空选区的代码较多,此处仅仅展示了鼠标的三个事件监听的相关代码,完整代码请移步:EventMonitoring.ts

    • 绘制裁剪框的代码如下
    /**
     * 绘制裁剪框
     * @param mouseX 鼠标x轴坐标
     * @param mouseY 鼠标y轴坐标
     * @param width 裁剪框宽度
     * @param height 裁剪框高度
     * @param context 需要进行绘制的canvas画布
     * @param borderSize 边框节点直径
     * @param controller 需要进行操作的canvas容器
     * @param imageController 图片canvas容器
     * @private
     */
    export function drawCutOutBox(
      mouseX: number,
      mouseY: number,
      width: number,
      height: number,
      context: CanvasRenderingContext2D,
      borderSize: number,
      controller: HTMLCanvasElement,
      imageController: HTMLCanvasElement
    ) {
      // 获取画布宽高
      const canvasWidth = controller?.width;
      const canvasHeight = controller?.height;
    
      // 画布、图片不存在则return
      if (!canvasWidth || !canvasHeight || !imageController || !controller) return;
    
      // 清除画布
      context.clearRect(0, 0, canvasWidth, canvasHeight);
    
      // 绘制蒙层
      context.save();
      context.fillStyle = "rgba(0, 0, 0, .6)";
      context.fillRect(0, 0, canvasWidth, canvasHeight);
      // 将蒙层凿开
      context.globalCompositeOperation = "source-atop";
      // 裁剪选择框
      context.clearRect(mouseX, mouseY, width, height);
      // 绘制8个边框像素点并保存坐标信息以及事件参数
      context.globalCompositeOperation = "source-over";
      context.fillStyle = "#2CABFF";
      // 像素点大小
      const size = borderSize;
      // 绘制像素点
      context.fillRect(mouseX - size / 2, mouseY - size / 2, size, size);
      context.fillRect(
        mouseX - size / 2 + width / 2,
        mouseY - size / 2,
        size,
        size
      );
      context.fillRect(mouseX - size / 2 + width, mouseY - size / 2, size, size);
      context.fillRect(
        mouseX - size / 2,
        mouseY - size / 2 + height / 2,
        size,
        size
      );
      context.fillRect(
        mouseX - size / 2 + width,
        mouseY - size / 2 + height / 2,
        size,
        size
      );
      context.fillRect(mouseX - size / 2, mouseY - size / 2 + height, size, size);
      context.fillRect(
        mouseX - size / 2 + width / 2,
        mouseY - size / 2 + height,
        size,
        size
      );
      context.fillRect(
        mouseX - size / 2 + width,
        mouseY - size / 2 + height,
        size,
        size
      );
      // 绘制结束
      context.restore();
      // 使用drawImage将图片绘制到蒙层下方
      context.save();
      context.globalCompositeOperation = "destination-over";
      context.drawImage(
        imageController,
        0,
        0,
        controller?.width,
        controller?.height
      );
      context.restore();
      // 返回裁剪框临时位置信息
      return {
        startX: mouseX,
        startY: mouseY,
        width: width,
        height: height
      };
    }
    
    

    ⚠️同样的,注释写的很详细,上述代码用到的canvas API除了之前介绍的外,用到的新的API如下:globalCompositeOperationdrawImage

    实现截图工具栏

    我们实现镂空选区的相关功能后,接下来要做的就是在选区内进行圈选、框选、画线等操作了,在QQ的截图中这些操作位于截图工具栏内,因此我们要将截图工具栏做出来,做到与canvas交互。

    在截图工具栏的布局上,一开始我的想法是直接在canvas画布中把这些工具画出来,这样应该更容易交互一点,但是我看了相关的api后,发现有点麻烦,把问题复杂化了。

    琢磨了一阵后,想明白了,这块还是需要使用div进行布局的,在裁剪框绘制完毕后,根据裁剪框的位置信息计算出截图工具栏的位置,改变其位置即可。

    工具栏与canvas的交互,可以绑定一个点击事件到EventMonitoring.ts中,获取当前点击项,指定与之对应的图形绘制函数。

    实现的效果如下:

    222

    具体的实现过程如下:

    • screen-short.vue中,创建截图工具栏div并布局好其样式
    <template>
      <teleport to="body">
           <!--工具栏-->
        <div
          id="toolPanel"
          v-show="toolStatus"
          :style="{ left: toolLeft + 'px', top: toolTop + 'px' }"
          ref="toolController"
        >
          <div
            v-for="item in toolbar"
            :key="item.id"
            :class="`item-panel ${item.title} `"
            @click="toolClickEvent(item.title, item.id, $event)"
          ></div>
          <!--撤销部分单独处理-->
          <div
            v-if="undoStatus"
            class="item-panel undo"
            @click="toolClickEvent('undo', 9, $event)"
          ></div>
          <div v-else class="item-panel undo-disabled"></div>
          <!--关闭与确认进行单独处理-->
          <div
            class="item-panel close"
            @click="toolClickEvent('close', 10, $event)"
          ></div>
          <div
            class="item-panel confirm"
            @click="toolClickEvent('confirm', 11, $event)"
          ></div>
        </div>
      </teleport>
    </template>
    
    <script lang="ts">
    import eventMonitoring from "@/module/main-entrance/EventMonitoring";
    import toolbar from "@/module/config/Toolbar.ts";
    
    export default {
      name: "screen-short",
      setup(props: Record<string, any>, context: SetupContext<any>) {
        const event = new eventMonitoring(props, context as SetupContext<any>);
        const toolClickEvent = event.toolClickEvent;
        return {
          toolClickEvent,
          toolbar
        }
      }
    }
    </script>
    

    ⚠️上述代码仅展示了组件的部分代码,完整代码请移步:screen-short.vuescreen-short.scss

    截图工具条目点击样式处理

    截图工具栏中的每一个条目都拥有三种状态:正常状态、鼠标移入、点击,此处我的做法是将所有状态写在css里了,通过不同的class名来显示不同的样式。

    部分工具栏点击状态的css如下:

    .square-active {
      background-image: url("~@/assets/img/square-click.png");
    }
    
    .round-active {
      background-image: url("~@/assets/img/round-click.png");
    }
    
    .right-top-active {
      background-image: url("~@/assets/img/right-top-click.png");
    }
    

    一开始我想在v-for渲染时,定义一个变量,点击时改变这个变量的状态,显示每个点击条目对应的点击时的样式,但是我在做的时候却发现问题了,我的点击时的class名是动态的,没发通过这种形式来弄,无奈我只好选择dom操作的形式来实现,点击时传$event到函数,获取当前点击项点击时的class,判断其是否有选中的class,如果有就删除,然后为当前点击项添加class。

    实现代码如下:

    • dom结构
    <div
        v-for="item in toolbar"
        :key="item.id"
        :class="`item-panel ${item.title} `"
        @click="toolClickEvent(item.title, item.id, $event)"
    ></div>
    
    • 工具栏点击事件
      /**
       * 裁剪框工具栏点击事件
       * @param toolName
       * @param index
       * @param mouseEvent
       */
      public toolClickEvent = (
        toolName: string,
        index: number,
        mouseEvent: MouseEvent
      ) => {
        // 为当前点击项添加选中时的class名
        setSelectedClassName(mouseEvent, index, false);
      }
    
    • 为当前点击项添加选中时的class,移除其兄弟元素选中时的class
    import { getSelectedClassName } from "@/module/common-methords/GetSelectedCalssName";
    import { getBrushSelectedName } from "@/module/common-methords/GetBrushSelectedName";
    
    /**
     * 为当前点击项添加选中时的class,移除其兄弟元素选中时的class
     * @param mouseEvent 需要进行操作的元素
     * @param index 当前点击项
     * @param isOption 是否为画笔选项
     */
    export function setSelectedClassName(
      mouseEvent: any,
      index: number,
      isOption: boolean
    ) {
      // 获取当前点击项选中时的class名
      let className = getSelectedClassName(index);
      if (isOption) {
        // 获取画笔选项选中时的对应的class
        className = getBrushSelectedName(index);
      }
      // 获取div下的所有子元素
      const nodes = mouseEvent.path[1].children;
      for (let i = 0; i < nodes.length; i++) {
        const item = nodes[i];
        // 如果工具栏中已经有选中的class则将其移除
        if (item.className.includes("active")) {
          item.classList.remove(item.classList[2]);
        }
      }
      // 给当前点击项添加选中时的class
      mouseEvent.target.className += " " + className;
    }
    
    
    • 获取截图工具栏点击时的class名
    export function getSelectedClassName(index: number) {
      let className = "";
      switch (index) {
        case 1:
          className = "square-active";
          break;
        case 2:
          className = "round-active";
          break;
        case 3:
          className = "right-top-active";
          break;
        case 4:
          className = "brush-active";
          break;
        case 5:
          className = "mosaicPen-active";
          break;
        case 6:
          className = "text-active";
      }
      return className;
    }
    
    
    • 获取画笔选择点击时的class名
    /**
     * 获取画笔选项对应的选中时的class名
     * @param itemName
     */
    export function getBrushSelectedName(itemName: number) {
      let className = "";
      switch (itemName) {
        case 1:
          className = "brush-small-active";
          break;
        case 2:
          className = "brush-medium-active";
          break;
        case 3:
          className = "brush-big-active";
          break;
      }
      return className;
    }
    
    

    实现工具栏中的每个选项

    接下来,我们来看看工具栏中每个选项的具体实现。

    工具栏中每个图形的绘制都需要鼠标按下、移动、抬起这三个事件的配合下完成,为了防止鼠标在移动时图形重复绘制,这里我们采用"历史记录"模式来解决这个问题,我们先来看下重复绘制时的场景,如下所示:

    1212

    接下来,我们来看下如何使用历史记录来解决这个问题。

    • 首先,我们需要定义一个数组变量,取名为history
    private history: Array<Record<string, any>> = [];
    
    • 当图形绘制结束鼠标抬起时,将当前画布状态保存至history
      /**
       * 保存当前画布状态
       * @private
       */
      private addHistoy() {
        if (
          this.screenShortCanvas != null &&
          this.screenShortController.value != null
        ) {
          // 获取canvas画布与容器
          const context = this.screenShortCanvas;
          const controller = this.screenShortController.value;
          if (this.history.length > this.maxUndoNum) {
            // 删除最早的一条画布记录
            this.history.unshift();
          }
          // 保存当前画布状态
          this.history.push({
            data: context.getImageData(0, 0, controller.width, controller.height)
          });
          // 启用撤销按钮
          this.data.setUndoStatus(true);
        }
      }
    
    • 当鼠标处于移动状态时,我们取出history中最后一条记录。
      /**
       * 显示最新的画布状态
       * @private
       */
      private showLastHistory() {
        if (this.screenShortCanvas != null) {
          const context = this.screenShortCanvas;
          if (this.history.length <= 0) {
            this.addHistoy();
          }
          context.putImageData(this.history[this.history.length - 1]["data"], 0, 0);
        }
      }
    

    上述函数放在合适的时机执行,即可解决图形重复绘制的问题,接下来我们看下解决后的绘制效果,如下所示:

    0909

    实现矩形绘制

    在前面的分析中,我们拿到了鼠标的起始点坐标和鼠标移动时的坐标,我们可以通过这些数据计算出框选区域的宽高,如下所示。

    // 获取鼠标起始点坐标
    const { startX, startY } = this.drawGraphPosition;
    // 获取当前鼠标坐标
    const currentX = nonNegativeData(event.offsetX);
    const currentY = nonNegativeData(event.offsetY);
    // 裁剪框临时宽高
    const tempWidth = currentX - startX;
    const tempHeight = currentY - startY;
    

    我们拿到这些数据后,即可通过canvas的rect这个API来绘制一个矩形了,代码如下所示:

    /**
     * 绘制矩形
     * @param mouseX
     * @param mouseY
     * @param width
     * @param height
     * @param color 边框颜色
     * @param borderWidth 边框大小
     * @param context 需要进行绘制的canvas画布
     * @param controller 需要进行操作的canvas容器
     * @param imageController 图片canvas容器
     */
    export function drawRectangle(
      mouseX: number,
      mouseY: number,
      width: number,
      height: number,
      color: string,
      borderWidth: number,
      context: CanvasRenderingContext2D,
      controller: HTMLCanvasElement,
      imageController: HTMLCanvasElement
    ) {
      context.save();
      // 设置边框颜色
      context.strokeStyle = color;
      // 设置边框大小
      context.lineWidth = borderWidth;
      context.beginPath();
      // 绘制矩形
      context.rect(mouseX, mouseY, width, height);
      context.stroke();
      // 绘制结束
      context.restore();
      // 使用drawImage将图片绘制到蒙层下方
      context.save();
      context.globalCompositeOperation = "destination-over";
      context.drawImage(
        imageController,
        0,
        0,
        controller?.width,
        controller?.height
      );
      // 绘制结束
      context.restore();
    }
    
    

    实现椭圆绘制

    在绘制椭圆时,我们需要根据坐标信息计算出圆的半径、圆心坐标,随后调用ellipse函数即可绘制一个椭圆出来,代码如下所示:

    /**
     * 绘制圆形
     * @param context 需要进行绘制的画布
     * @param mouseX 当前鼠标x轴坐标
     * @param mouseY 当前鼠标y轴坐标
     * @param mouseStartX 鼠标按下时的x轴坐标
     * @param mouseStartY 鼠标按下时的y轴坐标
     * @param borderWidth 边框宽度
     * @param color 边框颜色
     */
    export function drawCircle(
      context: CanvasRenderingContext2D,
      mouseX: number,
      mouseY: number,
      mouseStartX: number,
      mouseStartY: number,
      borderWidth: number,
      color: string
    ) {
      // 坐标边界处理,解决反向绘制椭圆时的报错问题
      const startX = mouseX < mouseStartX ? mouseX : mouseStartX;
      const startY = mouseY < mouseStartY ? mouseY : mouseStartY;
      const endX = mouseX >= mouseStartX ? mouseX : mouseStartX;
      const endY = mouseY >= mouseStartY ? mouseY : mouseStartY;
      // 计算圆的半径
      const radiusX = (endX - startX) * 0.5;
      const radiusY = (endY - startY) * 0.5;
      // 计算圆心的x、y坐标
      const centerX = startX + radiusX;
      const centerY = startY + radiusY;
      // 开始绘制
      context.save();
      context.beginPath();
      context.lineWidth = borderWidth;
      context.strokeStyle = color;
    
      if (typeof context.ellipse === "function") {
        // 绘制圆,旋转角度与起始角度都为0,结束角度为2*PI
        context.ellipse(centerX, centerY, radiusX, radiusY, 0, 0, 2 * Math.PI);
      } else {
        throw "你的浏览器不支持ellipse,无法绘制椭圆";
      }
      context.stroke();
      context.closePath();
      // 结束绘制
      context.restore();
    }
    
    

    ⚠️注释已经写的很清楚了,此处用到的API有:beginPathlineWidthellipseclosePath,对这些API不熟悉的开发者请移步到指定位置进行查阅。

    实现箭头绘制

    箭头绘制相比其他工具来说是最复杂的,因为我们需要通过三角函数来计算箭头两个点的坐标,通过三角函数中的反正切函数来计算箭头的角度

    既然需要用到三角函数来实现,那我们先来看下我们的已知条件:

      /**
       * 已知:
       *    1. P1、P2的坐标
       *    2. 箭头斜线P3到P2直线的长度,P4与P3是对称的,因此P4到P2的长度等于P3到P2的长度
       *    3. 箭头斜线P3到P1、P2直线的夹角角度(θ),因为是对称的,所以P4与P1、P2直线的夹角角度是相等的
       * 求:
       *    P3、P4的坐标
       */
    
    image-20210201231116257

    如上图所示,P1为鼠标按下时的坐标,P2为鼠标移动时的坐标,夹角θ的角度为30,我们知道这些信息后就可以求出P3和P4的坐标了,求出坐标后我们即可通过canvas的moveTo、lineTo来绘制箭头了。

    实现代码如下:

    /**
     * 绘制箭头
     * @param context 需要进行绘制的画布
     * @param mouseStartX 鼠标按下时的x轴坐标 P1
     * @param mouseStartY 鼠标按下式的y轴坐标 P1
     * @param mouseX 当前鼠标x轴坐标 P2
     * @param mouseY 当前鼠标y轴坐标 P2
     * @param theta 箭头斜线与直线的夹角角度 (θ) P3 ---> (P1、P2) || P4 ---> P1(P1、P2)
     * @param headlen 箭头斜线的长度 P3 ---> P2 || P4 ---> P2
     * @param borderWidth 边框宽度
     * @param color 边框颜色
     */
    export function drawLineArrow(
      context: CanvasRenderingContext2D,
      mouseStartX: number,
      mouseStartY: number,
      mouseX: number,
      mouseY: number,
      theta: number,
      headlen: number,
      borderWidth: number,
      color: string
    ) {
      /**
       * 已知:
       *    1. P1、P2的坐标
       *    2. 箭头斜线(P3 || P4) ---> P2直线的长度
       *    3. 箭头斜线(P3 || P4) ---> (P1、P2)直线的夹角角度(θ)
       * 求:
       *    P3、P4的坐标
       */
      const angle =
          (Math.atan2(mouseStartY - mouseY, mouseStartX - mouseX) * 180) / Math.PI, // 通过atan2来获取箭头的角度
        angle1 = ((angle + theta) * Math.PI) / 180, // P3点的角度
        angle2 = ((angle - theta) * Math.PI) / 180, // P4点的角度
        topX = headlen * Math.cos(angle1), // P3点的x轴坐标
        topY = headlen * Math.sin(angle1), // P3点的y轴坐标
        botX = headlen * Math.cos(angle2), // P4点的X轴坐标
        botY = headlen * Math.sin(angle2); // P4点的Y轴坐标
    
      // 开始绘制
      context.save();
      context.beginPath();
    
      // P3的坐标位置
      let arrowX = mouseStartX - topX,
        arrowY = mouseStartY - topY;
    
      // 移动笔触到P3坐标
      context.moveTo(arrowX, arrowY);
      // 移动笔触到P1
      context.moveTo(mouseStartX, mouseStartY);
      // 绘制P1到P2的直线
      context.lineTo(mouseX, mouseY);
      // 计算P3的位置
      arrowX = mouseX + topX;
      arrowY = mouseY + topY;
      // 移动笔触到P3坐标
      context.moveTo(arrowX, arrowY);
      // 绘制P2到P3的斜线
      context.lineTo(mouseX, mouseY);
      // 计算P4的位置
      arrowX = mouseX + botX;
      arrowY = mouseY + botY;
      // 绘制P2到P4的斜线
      context.lineTo(arrowX, arrowY);
      // 上色
      context.strokeStyle = color;
      context.lineWidth = borderWidth;
      // 填充
      context.stroke();
      // 结束绘制
      context.restore();
    }
    
    

    ⚠️此处用到的新API有:moveTolineTo,对这些API不熟悉的开发者请移步到指定位置进行查阅。

    实现画笔绘制

    画笔的绘制我们需要通过lineTo来实现,不过在绘制时需要注意:在鼠标按下时需要通过beginPath来清空一条路径,并移动画笔笔触到鼠标按下时的位置,否则鼠标的起始位置始终是0,bug如下所示:

    1211

    那么要解决这个bug,就需要在鼠标按下时初始化一下笔触位置,代码如下:

    /**
     * 画笔初始化
     */
    export function initPencli(
      context: CanvasRenderingContext2D,
      mouseX: number,
      mouseY: number
    ) {
      // 开始||清空一条路径
      context.beginPath();
      // 移动画笔位置
      context.moveTo(mouseX, mouseY);
    }
    
    

    随后,再鼠标位置时根据坐标信息绘制线条即可,代码如下:

    /**
     * 画笔绘制
     * @param context
     * @param mouseX
     * @param mouseY
     * @param size
     * @param color
     */
    export function drawPencli(
      context: CanvasRenderingContext2D,
      mouseX: number,
      mouseY: number,
      size: number,
      color: string
    ) {
      // 开始绘制
      context.save();
      // 设置边框大小
      context.lineWidth = size;
      // 设置边框颜色
      context.strokeStyle = color;
      context.lineTo(mouseX, mouseY);
      context.stroke();
      // 绘制结束
      context.restore();
    }
    

    实现马赛克绘制

    我们都知道图片是由一个个像素点构成的,当我们把某个区域的像素点设置成同样的颜色,这块区域的信息就会被破坏掉,被我们破坏掉的区域就叫马赛克。

    知道马赛克的原理后,我们就可以分析出实现思路:

    • 获取鼠标划过路径区域的图像信息
    • 将区域内的像素点绘制成周围相近的颜色

    具体的实现代码如下:

    /**
     * 获取图像指定坐标位置的颜色
     * @param imgData 需要进行操作的图片
     * @param x x点坐标
     * @param y y点坐标
     */
    const getAxisColor = (imgData: ImageData, x: number, y: number) => {
      const w = imgData.width;
      const d = imgData.data;
      const color = [];
      color[0] = d[4 * (y * w + x)];
      color[1] = d[4 * (y * w + x) + 1];
      color[2] = d[4 * (y * w + x) + 2];
      color[3] = d[4 * (y * w + x) + 3];
      return color;
    };
    
    /**
     * 设置图像指定坐标位置的颜色
     * @param imgData 需要进行操作的图片
     * @param x x点坐标
     * @param y y点坐标
     * @param color 颜色数组
     */
    const setAxisColor = (
      imgData: ImageData,
      x: number,
      y: number,
      color: Array<number>
    ) => {
      const w = imgData.width;
      const d = imgData.data;
      d[4 * (y * w + x)] = color[0];
      d[4 * (y * w + x) + 1] = color[1];
      d[4 * (y * w + x) + 2] = color[2];
      d[4 * (y * w + x) + 3] = color[3];
    };
    
    /**
     * 绘制马赛克
     *    实现思路:
     *      1. 获取鼠标划过路径区域的图像信息
     *      2. 将区域内的像素点绘制成周围相近的颜色
     * @param mouseX 当前鼠标X轴坐标
     * @param mouseY 当前鼠标Y轴坐标
     * @param size 马赛克画笔大小
     * @param degreeOfBlur 马赛克模糊度
     * @param context 需要进行绘制的画布
     */
    export function drawMosaic(
      mouseX: number,
      mouseY: number,
      size: number,
      degreeOfBlur: number,
      context: CanvasRenderingContext2D
    ) {
      // 获取鼠标经过区域的图片像素信息
      const imgData = context.getImageData(mouseX, mouseY, size, size);
      // 获取图像宽高
      const w = imgData.width;
      const h = imgData.height;
      // 等分图像宽高
      const stepW = w / degreeOfBlur;
      const stepH = h / degreeOfBlur;
      // 循环画布像素点
      for (let i = 0; i < stepH; i++) {
        for (let j = 0; j < stepW; j++) {
          // 随机获取一个小方格的随机颜色
          const color = getAxisColor(
            imgData,
            j * degreeOfBlur + Math.floor(Math.random() * degreeOfBlur),
            i * degreeOfBlur + Math.floor(Math.random() * degreeOfBlur)
          );
          // 循环小方格的像素点
          for (let k = 0; k < degreeOfBlur; k++) {
            for (let l = 0; l < degreeOfBlur; l++) {
              // 设置小方格的颜色
              setAxisColor(
                imgData,
                j * degreeOfBlur + l,
                i * degreeOfBlur + k,
                color
              );
            }
          }
        }
      }
      // 渲染打上马赛克后的图像信息
      context.putImageData(imgData, mouseX, mouseY);
    }
    
    

    实现文字绘制

    canvas没有直接提供API来供我们输入文字,但是它提供了填充文本的API,因此我们需要一个div来让用户输入文字,用户输入完成后将输入的文字填充到指定区域即可。

    实现的效果如下:

    1258

    • 在组件中创建一个div,开启div的可编辑属性,布局好样式
    <template>
      <teleport to="body">
            <!--文本输入区域-->
        <div
          id="textInputPanel"
          ref="textInputController"
          v-show="textStatus"
          contenteditable="true"
          spellcheck="false"
        ></div>
      </teleport>
    </template>
    
    • 鼠标按下时,计算文本输入区域位置
    // 计算文本框显示位置
    const textMouseX = mouseX - 15;
    const textMouseY = mouseY - 15;
    // 修改文本区域位置
    this.textInputController.value.style.left = textMouseX + "px";
    this.textInputController.value.style.top = textMouseY + "px";
    
    • 输入框位置发生变化时代表用户输入完毕,将用户输入的内容渲染到canvas,绘制文本的代码如下
    /**
     * 绘制文本
     * @param text 需要进行绘制的文字
     * @param mouseX 绘制位置的X轴坐标
     * @param mouseY 绘制位置的Y轴坐标
     * @param color 字体颜色
     * @param fontSize 字体大小
     * @param context 需要你行绘制的画布
     */
    export function drawText(
      text: string,
      mouseX: number,
      mouseY: number,
      color: string,
      fontSize: number,
      context: CanvasRenderingContext2D
    ) {
      // 开始绘制
      context.save();
      context.lineWidth = 1;
      // 设置字体颜色
      context.fillStyle = color;
      context.textBaseline = "middle";
      context.font = `bold ${fontSize}px 微软雅黑`;
      context.fillText(text, mouseX, mouseY);
      // 结束绘制
      context.restore();
    }
    
    

    实现下载功能

    下载功能比较简单,我们只需要将裁剪框区域的内容放进一个新的canvas中,然后调用toDataURL方法就能拿到图片的base64地址,我们创建一个a标签,添加download属性,出发a标签的点击事件即可下载。

    实现代码如下:

    export function saveCanvasToImage(
      context: CanvasRenderingContext2D,
      startX: number,
      startY: number,
      width: number,
      height: number
    ) {
      // 获取裁剪框区域图片信息
      const img = context.getImageData(startX, startY, width, height);
      // 创建canvas标签,用于存放裁剪区域的图片
      const canvas = document.createElement("canvas");
      canvas.width = width;
      canvas.height = height;
      // 获取裁剪框区域画布
      const imgContext = canvas.getContext("2d");
      if (imgContext) {
        // 将图片放进裁剪框内
        imgContext.putImageData(img, 0, 0);
        const a = document.createElement("a");
        // 获取图片
        a.href = canvas.toDataURL("png");
        // 下载图片
        a.download = `${new Date().getTime()}.png`;
        a.click();
      }
    }
    
    

    实现撤销功能

    由于我们绘制图形采用了历史记录模式,每次图形绘制都会存储一次画布状态,我们只需要在点击撤销按钮时,从history弹出一最后一条记录即可。

    实现代码如下:

    /**
     * 取出一条历史记录
     */
    private takeOutHistory() {
      const lastImageData = this.history.pop();
      if (this.screenShortCanvas != null && lastImageData) {
        const context = this.screenShortCanvas;
        if (this.undoClickNum == 0 && this.history.length > 0) {
          // 首次取出需要取两条历史记录
          const firstPopImageData = this.history.pop() as Record<string, any>;
          context.putImageData(firstPopImageData["data"], 0, 0);
        } else {
          context.putImageData(lastImageData["data"], 0, 0);
        }
      }
    
      this.undoClickNum++;
      // 历史记录已取完,禁用撤回按钮点击
      if (this.history.length <= 0) {
        this.undoClickNum = 0;
        this.data.setUndoStatus(false);
      }
    }
    

    实现关闭功能

    关闭功能指的是重置截图组件,因此我们需要通过emit向父组件推送销毁的消息。

    实现代码如下:

      /**
       * 重置组件
       */
      private resetComponent = () => {
        if (this.emit) {
          // 隐藏截图工具栏
          this.data.setToolStatus(false);
          // 初始化响应式变量
          this.data.setInitStatus(true);
          // 销毁组件
          this.emit("destroy-component", false);
          return;
        }
        throw "组件重置失败";
      };
    

    实现确认功能

    当用户点击确认后,我们需要将裁剪框内的内容转为base64,然后通过emit推送给付组件,最后重置组件。

    实现代码如下:

    const base64 = this.getCanvasImgData(false);
    this.emit("get-image-data", base64);
    

    插件地址

    至此,插件的实现过程就分享完毕了。

    写在最后

    • 文中如有错误,欢迎在评论区指正,如果这篇文章帮到了你,欢迎点赞和关注😊
    • 未经许可禁止转载💌
    展开全文
  • 学习专用精品demo推荐:微信设计指南中的Web设计和控件库(源代码+截图)学习专用精品demo推荐:微信设计指南中的Web设计和控件库(源代码+截图)学习专用精品demo推荐:微信设计指南中的Web设计和控件库(源代码+截图)...
  • 主要介绍了编写Python脚本使得web页面上的代码高亮显示,主要使用了pygments工具,需要的朋友可以参考下
  • 传统的操作方式是:屏幕截图,保存文件到本地,在web页面上选择本地文件并上传,这里至少需要三步。有没有可能直接将截图粘帖到web页面上,然后上传?答案是:可以的。这就是本文要介绍的内容了。 由于我的项目有...
  • 主要介绍了WebDriver中实现对特定的Web区域截图方法,本文直接给出实现代码,需要的朋友可以参考下
  • 水晶球 :crystal_ball: 一个神奇的Web屏幕截图项目水晶球 :crystal_ball: 是一个使用URL数组或文件并返回包含屏幕截图(使用 ),应用程序标头和src / href引用的报告的库。 有关下载和安装Node.js的信息,请参见 。...
  • web实验2截图.docx

    2019-06-21 16:07:29
    简单的控件应用,.net开发实例代码,包含常见的前端控件使用代码
  • 您可以从源代码编译Web Plus,也可以使用预编译的二进制文件。 从来源 要构建Web Plus,您将需要以下依赖项: Python3x 。 在存储源代码的目录的根目录中,执行以下命令: pip3 install -r requirements.txt python...
  • 前段时间项目中需要用到截图,看了好多文章,实现像微信和QQ的截屏方式都得用一些插件(大佬除外),直接上代码吧。 注意:请先运行插件,再进行操作 插件下载地址:...

    web前端页面全屏截图技术实现(插件【牛牛截图】)

    前段时间项目中需要用到截图,看了好多文章,最终得到实现像微信和QQ的截屏方式都得用一些插件(大佬除外),然后找了一个插件还挺好用,直接上代码吧需要的直接粘贴就行。

    注意:请先运行插件,再进行操作
    插件下载地址:http://www.ggniu.cn/download/CaptureInstall.exe
    主要有三个js:https://download.csdn.net/download/jia814583973/15739899

    1.前端代码

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
        <script language="javascript" src="../js/jquery-1.6.4.min.js"></script>
        <script language="javascript" src="../js/niuniucapture.js"></script>
        <script language="javascript" src="../js/capturewrapper.js"></script>
        <script language="javascript" src="../js/jquery.json-2.3.min.js"></script>
        <style>
            #jp{
                width: 40px;
                height: 21px;
                background-color: #38B1B9;
            }
        </style>
    </head>
    <body>
    <div id="jp" class="capture1">截屏</div>
    </body>
    <script>
       $(function () {
           Init();
       });
       $('.capture1').click(function () {
           showModalFram();
       });
    
       function showModalFram(){
           if(selfConnectionState()!= 2){
               if(confirm("您好!此功能需要截屏,需要安装截屏插件\n\n请点击确认下载并手动安装?")){
                   selfConnection();//确定截屏插件安装完成,并连接成功
                   return;
               }else{
                   return;
               }
           }
           //自定义截图,传入截图后文件名
           var myDate = new Date();
           var year = myDate.getFullYear(),
               month = myDate.getMonth() + 1,
               dates = myDate.getDate(),
               hour = myDate.getHours() + '',
               min = myDate.getMinutes() + '',
               sec = myDate.getSeconds() + '';
           var mytime = "_"+year +
                           (month.length == 1 ? '0' + month : month)+
                           (dates.length == 1 ? '0' + dates : dates)+
                           (hour.length == 1 ? '0' + hour : hour) +
                           (min.length == 1 ? '0' + min : min) +
                           (sec.length == 1 ? '0' + sec : sec);
           console.log(mytime)
           StartUpdateCapture(mytime);
       }
       function selfConnection(){
           //获取当前牛牛截图连接状态
           captureObj.connectHost();
           if(selfConnectionState != 2){
               window.location="http://www.ggniu.cn/download/CaptureInstall.exe";
           }
       }
       function selfConnectionState(){
           console.log("connectionState:"+captureObj.connectState);
           return captureObj.connectState;
       }
    
       //自定义截图函数
       function StartUpdateCapture(fileName) {
           fileName = fileName+".jpg";
           var captureRet = captureUpdate(fileName);
           //从返回值来解析显示
           if(captureRet == emCaptureFailed || captureRet == emCaptureUnknown) {
            //window.location="http://www.ggniu.cn/download/CaptureInstall.exe";
           }
       }
       //把牛牛截图多个模式截图方式中选择全屏截图
       function captureUpdate(fileName){
           /**DoCapture参数说明:
            * 参数1:fileName 截图完成时自动保存的文件名
            * 参数2:是否要隐藏当前浏览器窗口(1表示截图时隐藏当前窗口,0表示截图时不隐藏当前窗口。)
            * 参数3:标识截图的方式(0:表示普通截图
                                1:表示截取指定区域,区域由x、y、width、height参数指定
                                2:表示截取当前桌面
                                3: 表示截图时先弹出一个提示窗口
                                4: 从剪贴板中获取图片)
            * 参数4/5/6/7:参数3autoCapture的值为1时表示自动截取指定区域。
                                当参数3autoCapture为3时:
                                a. 如果全0,则弹出预截图窗口后再选择区域
                                b. 如果全是1,则弹出预截图窗口后,自动截取整个桌面
                                c. 其他情况,弹出预截图窗口后,自动截取指定的区域
            **/
           return captureObj.DoCapture(fileName, 1, 3, 0, 0, 0, 0);
       }
    
       //控制上传
       function UploadCaptureData(content, localpath)
       {
           savedPictureContent = content;
            //获取图片的扩展名
           var pos = localpath.lastIndexOf('.');
           extendName = localpath.substr(pos + 1);
           var fileName = localpath.substr(localpath.lastIndexOf('\\')+1);
           UploadData(fileName);
           /*$('#show').html('截图已经完成,请点击');
           $('#show').show();
           var autoUpload = $("#autoupload").attr("checked")=="checked" ? 1 : 0;
           if(autoUpload)
           {
           UploadData();
           }
           else
           {
           $('#btnUpload').show();
           } */
       }
    
       //实际上传图像数据的函数,此处主要是将BASE64的图像数据,通过AJAX的方式POST到服务器保存成文件,并且显示在页面上
       function UploadData(fileName) {
           //上传的数据除了图片外,还可以包含自己需要传递的参数
           var dataPra = "fileName="+fileName+"&picdata=" + savedPictureContent;
           //获取路径中项目名称
           $.ajax({
               type: "POST",
               url: "/jp/uploadScreenshotPicture",
               dataType: "json",
               data: dataPra,
               success: function () {
                   alert("上传成功");
               }
           });
       }
    </script>
    </html>
    

    2.java代码

    package com.jdl.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.RequestMethod;
    import org.springframework.web.bind.annotation.ResponseBody;
    import sun.misc.BASE64Decoder;
    
    import javax.servlet.http.HttpServletRequest;
    import java.io.File;
    import java.io.FileOutputStream;
    
    /**
     * @Descriptions 牛牛截屏
     * @Author:JDL
     * @Time:2021/3/11 15:11
     */
    @Controller
    @RequestMapping(value = "/jp")
    public class JpController {
    
        @RequestMapping(value = "uploadScreenshotPicture",method = RequestMethod.POST)
        @ResponseBody
        public void uploadScreenshotPicture(HttpServletRequest request){
            try{
                String picdata = request.getParameter("picdata").replaceAll(" ", "+");
                String fileName = request.getParameter("fileName");
                //fileName = "截屏_"+ DateHelper.getNow();
                //项目在本地的路径
                String systemPath =request.getSession().getServletContext().getRealPath("");
                //File screenshotFile = new File(systemPath+"\\uploadPath\\screenshotPicture");
                File screenshotFile = new File("D:/upload1/jp");
                if(!screenshotFile.exists()){
                    screenshotFile.mkdirs();
                }
                //systemPath = systemPath+"\\uploadPath\\screenshotPicture";
                systemPath = "D:/upload1/jp";
                boolean flag = JpController.generateImage(picdata,systemPath+"/"+fileName);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        /**
         * base64字符串转化成图片
         * @param imgStr base64字符串
         * @param imagePath 转换图片地址
         * @return
         */
        public static boolean generateImage(String imgStr,String imagePath) {
            if (imgStr == null){//图像数据为空
                return false;
            }
            BASE64Decoder decoder = new BASE64Decoder();
            try {
                byte[] decodeBuffer = decoder.decodeBuffer(imgStr);
                FileOutputStream fileOutputStream = new FileOutputStream(imagePath);
                fileOutputStream.write(decodeBuffer);
                fileOutputStream.flush();
                fileOutputStream.close();
                return true;
            }
            catch (Exception e) {
                return false;
            }
        }
    }
    
    
    展开全文
  • web4截图.docx

    2019-06-21 16:10:35
    web开发代码。net,综合使用实例,请谨慎下载代码 ‘’
  • 韩顺平 在线web QQ代码

    2013-10-18 19:00:29
    资源是韩顺平讲师的关于在线在线web QQ的代码,自己边看边截屏的,很辛苦的
  • 本文实例展示了asp.net截屏功能实现截取web页面的方法,代码简洁易懂,分享给大家供大家参考。 具体实现代码如下: using System.Drawing; //打开该页面 System.Diagnostics.Process.Start(IEXPLORE.EXE, ...
  • 代码截屏与禁止截屏

    千次阅读 2017-06-14 15:05:49
    今天看慕课网的视频,其中使用了代码截屏,鉴于一起没有用过特此记录一下下 很简单的几行代码这里我就手敲了不传代码片了 //获取activity最顶层窗口的view View view=getWindow().getDecorView(); //设置...

     今天看慕课网的视频,其中使用了代码截屏,鉴于一起没有用过特此记录一下下大笑

    很简单的几行代码这里我就手敲了不传代码片了


    //获取activity最顶层窗口的view

    View view=getWindow().getDecorView();

    //设置控件允许绘制缓存

    view.setDrawingCashEnabled(true);

    //获取控件的绘制缓存(快照)

    view.buildDrawingCache();

    //把获取到的快照转换为Bitmap的图片形式

    Bitmap imageResult=view.getDrawingCache();



    我们还会遇到不允许截屏的情况

    //设置activity不允许截屏

    activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_SECURE);

    展开全文
  • 贝岭的matlab的代码 BelleFemme screen shots (dashboard) for the web application belle-femme belle-femme.tn --> to visite the application
  • 稻草人为数据科学提供低代码Web开发。 演示版 [截图] 入门 什么是稻草人? Scarecrow为构建数据科学应用程序提供了一个免费且完全自定义的打包框架,该框架可根据需要随心所欲地进行定制。 有许多用于Web开发的框架...
  • 今天小编就为大家分享一篇关于Selenium的UI自动化测试屏幕截图功能实例代码,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 利用它,你可以将一些重复性的任务实现自动化、并且它可以进行界面截图、抓取网站数据、你还可以自定义时间何时去执行自动化任务等。 2. Automa安装 听了上述介绍,想必你已经跃跃欲试了。 如果你
  • 内容索引:脚本资源,jQuery,图片裁切, ImageCropper,jQuery插件 基于jquery的图片截取工具 jquery imagecropper,可自定义截取区域的位置、大孝并可拖动滑块放大、缩小图片,与以往的图片截取功能相比,这个更实用,...
  • 一、窗口截图的方法 driver.get_screenshot_as_file(路径+图片名) 后缀名推荐使用.png,路径推荐使用相对路径。 二、脚本重复执行,后面的截图会覆盖原有的...如果代码执行没有报错时截图失败,要么是图片名错误...
  • web端video截图和录屏功能的实现 前言 平常在用视频app,特别是一些摄像头监控软件的时候,经常会对直播、回放的视频进行抓拍录屏的操作。今天就来实现一下这两个功能 思路 截图: 主要是利用canvas的drawImage和...
  • 括号 这是Brackets的单元, “用于HTML,CSS和JavaScript的现代代码编辑器”。 。 它适用于Ubuntu,Fedora,Debian和... 将屏幕截图添加到此README.md 在Snap商店边缘通道中发布devmode快照 将安装说明添加到此READ
  • 打开Web代码检查器最受欢迎的支持问题是:“我必须编辑哪些CSS代码才能改变(…)的外观”。几年前,CSS/HTML编辑器是最有用的web开发工具。现在,我认为更有用的是构建在每个现代浏览器中的web代码调试器,例如Firefox...
  • WebDriver中实现对特定的Web区域截图方法用过 WebDriver 的同学都知道,WebDriver 可以对浏览器中的页面进行截图。例如:public byte[] takeScreenshot() throws IOException {TakesScreenshot takesScreenshot = ...
  • 代码分为客户端和服务端,客户端实现拍照后照片的上传,服务端接收上传至服务器的照片并显示,代码正确无误,可以正常运行!
  • Web组态——新一代全流程低代码物联网平台
  • HTML+CSS淘宝首页[web课设代码+模块说明+效果图](共1800行代码)

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 95,080
精华内容 38,032
热门标签
关键字:

web代码截屏