Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 最近中文字幕完整版视频,欧美毛片大全,亚洲日本在线观看视频

          整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          實現HTML5網站中常見的拖拽上傳文件

          實現HTML5網站中常見的拖拽上傳文件

          我們學習了 HTML 提供的原生拖放(drag & drop)后,是時候想一想這個東西可以用來作什么可以在什么時候使用使用的場景等等

          場景分析

          當我們在注冊成功一個賬戶時,一般網站會讓我們上傳我們的用戶頭像,或者在實名認證的時候會涉及到身份證圖片上傳到等,這時候我們可以使用input提供的file屬性進行選擇本地文件進行上傳。

          我們再想一下,當在電腦端的情況下,當用戶打開文件選擇框時再尋找圖片對應的文件夾,再進行選取文件的時候是不是會有點麻煩呢?我們可不可以讓用戶找到圖片文件,直接引入實現上傳呢?答案是可以的。

          怎么做

          經過這些分析后,我們可以嘗試使用 HTML5 提供的拖拽,使得目標元素增加讀取文件功能,然后使用 ajax 實現圖片上傳。

          談一談我們需要使用到的技術:

          • Drag & Drop: HTML5 基于拖拽的事件機制
          • File API: 可以很方便的讓 Web 應用訪問文件對象,File API 包括 FileList、Blob、File、FileReader、URI scheme,本文主要講解拖拽上傳中用到的 FileList 和 FileReader 接口。
          • FormData: FormData 是基于 XMLHttpRequest Level 2 的新接口,可以方便 web 應用模擬 Form 表單數據,最重要的是它支持文件的二進制流數據,這樣我們就能夠通過它來實現 AJAX 向后端發送文件數據了。

          HTML5 拖拽事件

          關于 Drag & Drop 拖拽事件,之前我寫過一篇專門介紹的文章,HTML5-拖拽,大家有興趣的話可以點擊鏈接查看,我在這里就不在多啰嗦了~下面直接出拖拽上傳的簡要代碼示例

          var oDragWrap=document.body;
          //拖進
          oDragWrap.addEventListener(
           "dragenter",
           function(e) {
           e.preventDefault();
           },
           false
          );
          //拖離
          oDragWrap.addEventListener(
           "dragleave",
           function(e) {
           dragleaveHandler(e);
           },
           false
          );
          //拖來拖去 , 一定要注意dragover事件一定要清除默認事件
          //不然會無法觸發后面的drop事件
          oDragWrap.addEventListener(
           "dragover",
           function(e) {
           e.preventDefault();
           },
           false
          );
          //扔
          oDragWrap.addEventListener(
           "drop",
           function(e) {
           dropHandler(e);
           },
           false
          );
          var dropHandler=function(e) {
           //將本地圖片拖拽到頁面中后要進行的處理都在這
          };
          

          獲取文件數據 HTML5 File API

          File API 中的 FileReader 接口,作為 File API 的一部分,FileReader 專門用來讀取文件。我們在這里主要介紹一些 File API 中的 FileList 接口,它主要通過兩個途徑獲取本地文件列表,一是<input type="file"/>的表單形式,另一種則是e.dataTransfer.files拖拽事件傳遞的文件信息。

          var fileList=e.dataTransfer.files;
          

          使用 files 方法將會獲取到拖拽文件的數組形式的數據,每個文件占用一個數組的索引,如果索引不存在文件數據,將返回 Null。可以通過length屬性獲取文件的數量。

          var fileNum=fileList.length;
          

          拖拽上傳需要注意的是需要判斷兩個條件

          1. 拖拽的是文件而不是頁面的元素
          2. 拖拽的是圖片而不是其他類型的文件,可以通過 file.type 屬性獲取文件的類型
          // 檢測是否是拖拽文件到頁面的操作
          if (fileList.length===0) {
           return;
          }
          // 檢測文件是不是圖片
          if (fileList[0].type.indexOf("image")===-1) {
           return;
          }
          

          下面我們看看結合之前的拖拽事件,來實現拖拽圖片并在頁面中預覽

          var dropHandler=function(e) {
           e.preventDefault(); //獲取文件列表
           var fileList=e.dataTransfer.files;
           //檢測是否是拖拽文件到頁面的操作
           if (fileList.length==0) {
           return;
           }
           //檢測文件是不是圖片
           if (fileList[0].type.indexOf("image")===-1) {
           return;
           }
           //實例化file reader對象
           var reader=new FileReader();
           var img=document.createElement("img");
           reader.onload=function(e) {
           img.src=this.result;
           oDragWrap.appendChild(img);
           };
           reader.readAsDataURL(fileList[0]);
          };
          

          當完成以上操作后,相信你可以成功的完成了拖拽圖片預覽的操作。當你查看 img 標簽時會發現,img的src屬性是一個超長的文件二進制數據,當你需要很多這種的img元素時,建議將展示區域脫離文檔流,讓其絕對定位減少頁面的 reflow

          AJAX 上傳圖片

          既然已經獲取到拖拽到web頁面中的圖片數據了,下一步就是將其發送到服務器端。

          總結

          1. 監聽拖拽: 監聽頁面元素的拖拽事件,包括: dragenter、dragover、dragleave 和drop,一定要將dragover的默認事件取消掉,不然無法觸發drop事件。如需拖拽頁面里面的元素,需要給其添加屬性draggable="true"
          2. 獲取拖拽文件: 在 drop 事件觸發后通過e.dataTransfer.files獲取拖拽文件列表,一定要將drop的默認事件取消掉,否則會默認打開文件length屬性獲取文件數量,type屬性獲取文件類型
          3. 讀取圖片數據并添加預覽圖: 實例化FileReader對象,通過其readAsDataURL(file)方法獲取文件二進制流,并監聽其onload事件,將e.result賦值給img的src屬性,最后將圖片添加到DOM中
          4. 發送圖片數據

          最近沒有更新文章,因為去字節實習了一陣,實在是沒有精力寫東西,所以就咕咕咕了。現在回學校了,就可以繼續更新啦,因為在字節做的業務和圖可視化還有拖拽關系比較大,所以這次就寫下拖拽相關的內容。

          HTML5 Drag and Drop 接口

          html5中提供了一系列Drag and Drop 接口,主要包括四部分:DragEventDataTansferDataTransferItemDataTransferItemList

          DragEvent

          源元素和目標元素

          image-20220314095928431.png

          **源元素:**即被拖拽的元素。

          **目標元素:**即合法的可釋放元素。

          每個事件的事件主體都是兩者之一。

          拖拽事件

          事件事件處理程序事件主體觸發時機dragstartondragstart源元素當源元素開始被拖拽。dragondrag源元素當源元素被拖拽(持續觸發)。dragendondragend源元素當源元素拖拽結束(鼠標釋放或按下esc鍵)dragenterondragenter目標元素當被拖拽元素進入該元素。dragoverondragover目標元素當被拖拽元素停留在該元素(持續觸發)。dragleaveondragleave目標元素當被拖拽元素離開該元素。dropondrop目標元素當拖拽事件在合法的目標元素上釋放。

          觸發順序及次數

          我們綁定相關的事件,拖放一次來查看相關事件的觸發情況。

          op.gif

          我們讓相應事件處理程序打印事件名稱及事件觸發的主體是誰,下面截取部分展示。

          image-20220314103603324-16473183157994.png

          我們可以看到對于被拖拽元素,事件觸發順序是 dragstart->drag->dragend;對于目標元素,事件觸發的順序是 dragenter->dragover->drop/dropleave

          其中dragdragover會分別在源元素和目標元素反復觸發。整個流程一定是dragstart第一個觸發,dragend最后一個觸發。

          這里還有一個注意的點,如果某個元素同時設置了dragoverdrop的監聽,那么必須阻止dragover的默認行為,否則drop將不會被觸發。

          image-20220314111402189.png

          DataTansfer

          我們先用一張圖來直觀的感受一下:

          image-20220315122157204-16473183290157.png

          我們可以看到,DataTransfer如同它的名字,作用就是在拖放過程中對數據進行傳輸,其中setData用來存放數據,getData用來獲取數據,出于安全的考量,數據只能在drop時獲取,而effectAlloweddropEffect則影響鼠標展示的樣式,下面我們用一個例子來進行展示:

          sourceElem.addEventListener('dragstart', (event) => {
              event.dataTransfer.effectAllowed = 'move';
              event.dataTransfer.setData('text/plain', '放進來了');
          });
          targetElem.addEventListener('dragover', (event) => {
              event.preventDefault();
              event.dataTransfer.dropEffect = 'move';
          });
          targetElem.addEventListener('drop', (event) => {
              event.target.innerHTML = event.dataTransfer.getData('text/plain');
          });
          復制代碼

          drag2.gif

          可以看到在藍色方塊設置的數據被成功取得了。

          DataTransferItemList

          屬性

          length: 列表中拖動項的數量。

          方法

          add(): 向拖動項列表中添加新項 (File對象或String),該方法返回一個 DataTransferItem) 對象。

          remove(): 根據索引刪除拖動項列表中的對象。

          clear(): 清空拖動項列表。

          DataTransferItem(): 取值方法:返回給定下標的DataTransferItem對象.

          DataTransferItem

          屬性

          kind: 拖拽項的種類,string 或是 file

          type: 拖拽項的類型,一般是一個MIME 類型。

          方法

          getAsString: 使用拖拽項的字符串作為參數執行指定回調函數。

          getAsFile: 返回一個關聯拖拽項的 File 對象 (當拖拽項不是一個文件時返回 null)。

          實踐

          學習了上面的基礎知識,我們從幾個常見的應用場景入手,來實踐上面的知識

          可放置組件

          知道上面幾個事件后,我們來完成一個簡單可放置組件,為了方便大家理解,這里不使用任何框架,以免增加不會框架同學的學習成本。

          想讓組件可拖行,那么就要可以改變它的位置,有兩種思路:

          • pos:abs通過top/left等直接改變元素的位置
          • 使用css的transform屬性中的translate對元素的位置進行改變

          我推薦第二種,首先translate是基于本身的移動,因此自身的坐標就作為原點(0,0),但是第一種,元素本身的top/left等可能并不為0,計算起來比較復雜。其次,第一種是通過cpu去計算,而第二種是通過gpu去計算,并且會提升到一個新的層,這樣做非常有利于頁面的性能。原因是 Chrome 這樣將 DOM 轉變成一個屏幕圖像:

          1. 獲取 DOM 并將其分割為多個層
          2. 將層作為紋理上傳至 GPU
          3. 復合多個層來生成最終的屏幕圖像。

          但更新的幀可以走捷徑,不必經歷所有過程:

          如果某些特定 CSS 屬性變化,并不需要發生重繪。Chrome 可以使用早已作為紋理而存在于 GPU 中的層來重新復合,但會使用不同的復合屬性(例如,出現在不同的位置,擁有不同的透明度等等)。

          如果圖層中某個元素需要重繪,那么整個圖層都需要重繪 。所以提升為一個新的層,可以減少重繪的次數。因為只改變位置,所以可以復用紋理,提高性能。

          更詳細的可以看我的另一篇文章:瀏覽器事件循環與渲染機制 \- 掘金 \(juejin.cn\)[1]

          有了思路那么我們就開始吧!

          首先我們要知道這次拖拽的向量是怎樣的,因為DragEvent繼承自MouseEvent ,所以我們可以通過MouseEvent接口的offsetX屬性和offsetY屬性獲取鼠標現在相對于該物體的位置差。而transform設置多個屬性值,效果就可以疊加,所以我們要獲得之前的移動效果,再加上現在的移動效果即可,之前的移動效果可以通過window.getComputedStyle(e.target).transform獲得。

          sourceElem.addEventListener('dragend', (e) => {
              const startPosition = window.getComputedStyle(e.target).transform;
              e.target.style.transform = `${startPosition} translate(${e.offsetX}px, ${e.offsetY}px)`;
          }, true);
          復制代碼

          我們給要拖拽的元素加上這段處理程序似乎就大功告成了。

          wrong.gif

          但是實際使用時,這個元素并沒有停在預覽的位置,而是左上角移動到鼠標的位置,顯然不符合預期,相信大家都能猜到,我們少考慮了鼠標在元素的位置,而鼠標初始的位置同樣可以通過MouseEvent接口的offsetX屬性和offsetY屬性獲取(dragstart)。改善如下:

          function enableDrag(element) {
              let mouseDiff = null;
              element.addEventListener('dragstart', (e) => {
                  //初始時鼠標與元素的位置差
                  mouseDiff = `translate(${-e.offsetX}px, ${-e.offsetY}px)`
              }, true);
              element.addEventListener('dragend', (e) => {
                  //開始時元素的位置
                  const startPosition = window.getComputedStyle(e.target).transform;
                  //鼠標移動的位置
                  const mouseMove = `translate(${e.offsetX}px, ${e.offsetY}px)`;
                  e.target.style.transform = `${mouseDiff} ${startPosition} ${mouseMove}`;
              }, true);
          }
          enableDrag(souceElement);
          復制代碼

          drag.gif

          圖的連線

          節點使用DOM渲染,連線我們使用SVG來渲染,框架使用React,但除了state盡量使用較少的框架相關的,以防非React技術棧的同學看不懂。

          首先我們要先組織我們的state,作為一個圖,顯然應該由nodesedges兩部分組成,我們都使用數組存儲,我們給每個node一個唯一的id,使用Map去映射id與對應的positon 形如 [x,y]的關系,而edge有源端與終端的id,通過id去獲得對應的坐標。

          我們先假設節點可以完成所有功能了,只考慮連線,可以定義如下的組件

          const Edge = ({nodes:[sourceNode,targetNode]}) =>(
              <svg key={sourceNode.id + targetNode.id||''} 
                  style={{position:'absolute',
                  overflow:'visible',
                  zIndex:'-1',
                  transform:'translate(15px,15px)'}}>
                  <path d={`M ${sourceNode.position[0]} ${sourceNode.position[1]} 
                      C
                      ${(targetNode.position[0]  + sourceNode.position[0])/2} ${sourceNode.position[1]}
                      ${(targetNode.position[0]  + sourceNode.position[0])/2} ${targetNode.position[1]}
                      ${targetNode.position[0]} ${targetNode.position[1]} `}
                      strokeWidth={6}
                      stroke={'red'}
                      fill='none'
                  ></path>
              </svg>
          )
          復制代碼

          首先我們應該從什么時候生成一個連線呢,顯然是dragstart,但這時還沒有對應的終端,因此不應該通過加入edges來循環渲染,而是單獨渲染一個出來。在dragstart我們設置一個虛擬節點temNode,并記錄開始節點的id。

          并如果有temNode則渲染一條預覽的edge。

          temNode && (<Edge nodes = {[sourceNode,temNode]}></Edge>)
          復制代碼

          drag3.gif

          然后加入這條edge后,我們刪除虛擬節點,變為循環渲染來展示所有的邊。

          edges.map(([sourceId,targetId])=>{
              const sourceNode = getNode(sourceId);
              const targetNode = getNode(targetId);
              return (
                  <Edge nodes = {[sourceNode,targetNode]}></Edge>);
          })
          復制代碼

          那么我們只考慮邊的展示了,節點的功能應該如何完善呢?

          首先是dragstart,我們要設置起始節點和虛擬節點

          onDragStart={()=>{
              setStartNodeId(uid);
              setTemNode({position:[x,y]})
          }}
          復制代碼

          然后既然邊可以跟著動,我們必然要在drag中動態的改變虛擬節點的位置

          onDrag={(event)=>{
              position=[x+event.nativeEvent.offsetX,y+event.nativeEvent.offsetY];
              setTemNode({position})
          }}
          復制代碼

          然后drop時,我們加入一條新的邊

          onDrop={
              (event)=>{
                  event.preventDefault();
                  setEdges(edges.concat([[startNodeId,uid]]))
              }
          }
          復制代碼

          最重要的是,不論在哪里事件結束了,要刪除虛擬節點

          onDragEnd={
              ()=>{
                  setTemNode(null);
              }
          }
          復制代碼

          下面是最終成果:

          drag4.gif

          參考

          • HTML Drag and Drop API - Web APIs | MDN \(mozilla.org\)[2]


          關于本文

          作者:灰兔呀

          https://juejin.cn/post/7075918201359433758

          開源精選》是我們分享Github、Gitee等開源社區中優質項目的欄目,包括技術、學習、實用與各種有趣的內容。本期推薦的是 AJ-Report是一個完全開源,拖拽編輯的可視化設計工具。三步快速完成大屏:配置數據源---->寫SQL配置數據集---->拖拽生成大屏。讓管理層隨時隨地掌控業務動態,讓每個決策都有數據支撐。

          簡介

          1. 最新最穩定的技術棧;
          2. 支持多數據源配置
          3. 豐富的大屏組件。拖拽配置實現動態大屏

          功能概述

          數據流程圖

          打包目錄

           bin                                           啟動命令腳本
          │   ├── restart.sh
          │   ├── start.bat
          │   ├── start.sh
          │   └── stop.sh
          ├── conf                                       配置文件目錄
          │   └── bootstrap-dev.yml
          ├── logs                                          啟動日志目錄
          ├── cache                                         本地緩存目錄
          ├── lib                                           自定義擴展包&report-core核心包

          系統目錄

          ├── doc                                           文檔源碼
          │   ├── docs
          │   ├── package.json
          │   └── README.md
          ├── pom.xml                                       父pom,jar版本管理
          ├── report-core                                   java源碼
          │   ├── pom.xml                                   gaea父pom,jar版本管理
          │   └── README.md
          ├── report-ui                                     前端vue源碼
          ├── LICENSE
          ├── README.md

          快速入門

          語言

          • [Mysql] 5.7
          • [Jdk] 1.8

          后端

          • Spring Boot: Spring Boot是一款開箱即用框架,讓我們的Spring應用變得更輕量化、更快地入門。 在主程序執行main函數就可以運行。你也可以打包你的應用為jar并通過使用java -jar來運行你的Web應用;
          • Mybatis-plus: MyBatis-plus(簡稱 MP)是一個 MyBatis (opens new window) 的增強工具。
          • flyway: 主要是在應用版本不斷升級的同時,升級你的數據庫結構和里面的數據

          前端

          • npm:node.js的包管理工具,用于統一管理我們前端項目中需要用到的包、插件、工具、命令等,便于開發和維護。
          • webpack:用于現代 JavaScript 應用程序的_靜態模塊打包工具
          • ES6:Javascript的新版本,ECMAScript6的簡稱。利用ES6我們可以簡化我們的JS代碼,同時利用其提供的強大功能來快速實現JS邏輯。
          • vue-cli:Vue的腳手架工具,用于自動生成Vue項目的目錄及文件。
          • vue-router: Vue提供的前端路由工具,利用其我們實現頁面的路由控制,局部刷新及按需加載,構建單頁應用,實現前后端分離。
          • vuex:Vue提供的狀態管理工具,用于統一管理我們項目中各種數據的交互和重用,存儲我們需要用到的數據對象。
          • element-ui:基于MVVM框架Vue開源出來的一套前端ui組件。
          • avue: 用該組件包裹后可以變成拖拽組件,采用相對于父類絕對定位;用鍵盤的上下左右也可以控制移動

          權限管理

          新增用戶

          用戶授權

          :這里沒有給新用戶賦予默認角色的原因是,在角色管理中角色是可以被刪除和修改的,因此在新建用戶時需要手動的去授權角色

          角色管理

          報表示例

          表格報表

          文檔:https://report.anji-plus.com/report-doc/


          主站蜘蛛池模板: 91秒拍国产福利一区| 91在线一区二区| 亚洲一区免费在线观看| 无码人妻久久一区二区三区免费| 无码精品人妻一区二区三区人妻斩 | 午夜福利一区二区三区高清视频 | 曰韩精品无码一区二区三区| 久久国产一区二区| 一区二区三区在线观看视频| 一区二区不卡在线| 亚洲AV无码一区二区二三区入口 | 综合人妻久久一区二区精品| 亚洲国产AV一区二区三区四区| 国产人妖视频一区二区| 精品国产一区二区三区香蕉事 | 无码人妻精品一区二区三区久久| 国产一区二区视频免费| 亚洲高清一区二区三区| 91秒拍国产福利一区| 91精品国产一区| 在线精品一区二区三区电影| 色综合视频一区中文字幕| 日本精品一区二区久久久| 亚洲av无码天堂一区二区三区| 亚洲韩国精品无码一区二区三区| 国产午夜一区二区在线观看| 国产午夜精品一区二区三区| 国产成人综合亚洲一区| 无码中文字幕乱码一区| 精品无码国产一区二区三区AV| 国产成人无码精品一区在线观看| 日韩美女视频一区| 国产日韩视频一区| 九九无码人妻一区二区三区 | 国产一区二区三区在线观看免费| 国产精品香蕉在线一区| 日美欧韩一区二去三区| 国产一区二区三区四| 丰满人妻一区二区三区免费视频 | 亚洲一区二区三区电影| 香蕉久久av一区二区三区|