vue iview多张图片大图预览、缩放翻转


Posted in Javascript onJuly 13, 2019

本文实例为大家分享了vue iview多张图片大图预览,可缩放翻转,供大家参考,具体内容如下

先看效果:

vue iview多张图片大图预览、缩放翻转

完整项目代码地址

具体代码如下:

<style lang="less">
@import "../advanced-router.less";
</style>
 
<template>
 <div class="balance-accounts">
  <Row class-name="detail-row">
   <Card>
    <p slot="title">
     回单照片
    </p>
    <div class="demo-upload-list" v-for="(item,index) in opPicsList" :key="index">
     <img :src="item.url">
     <div class="demo-upload-list-cover">
      <Icon type="ios-search-strong" @click.native="handleView(item.name)"></Icon>
     </div>
    </div>
   </Card>
  </Row>
 </div>
</template>
<script>
import * as API from "@/api/adminApi";
import iconLeft from "@/images/icon-left.png";
import iconRight from "@/images/icon-right.png";
import iconClose from "@/images/icon-close.png";
import iconRotate from "@/images/icon-rotate.png";
import iconNoImages from "@/images/icon-no-images.png";
 
export default {
 name: "shopping-info",
 data() {
 return {
  opPicsList: [
  {
   name: "none",
   url: iconNoImages
  }
  ],
  bigImage: null,
  currentImageName: "",
  currentRotate: 0
 };
 },
 props: ['imageList'],
 methods: {
 loadDetail() {
  let vm = this;
  API.getFundClearDetail({ orderId: this.$route.query.orderId }).then(
  data => {
   if (data.resultObj.detail) {
   if (data.resultObj.detail.opPics.length > 0) {
    vm.opPicsList.splice(0, vm.opPicsList.length);
    data.resultObj.detail.opPics
    .split(",")
    .forEach((element, index) => {
     vm.opPicsList.push({
     name: index,
     url: element
     });
    });
   }
   }
  }
  );
 },
 rollImg() {
  /* 获取当前页面的缩放比
   若未设置zoom缩放比,则为默认100%,即1,原图大小
  */
  var zoom = parseInt(this.bigImage.style.zoom) || 100;
  /* event.wheelDelta 获取滚轮滚动值并将滚动值叠加给缩放比zoom
   wheelDelta统一为±120,其中正数表示为向上滚动,负数表示向下滚动
  */
  zoom += event.wheelDelta / 12;
  /* 如果缩放比大于0,则将缩放比加载到页面元素 */
  if (zoom >= 100) {
  this.bigImage.style.zoom = zoom + "%";
  }
  /* 如果缩放比不大于0,则返回false,不执行操作 */
  return false;
 },
 handleView(name) {
  if (this.opPicsList[0].name == "none") {
  this.$Message.error("没有图片哦~");
  return;
  }
  this.currentImageName = name;
 
  let elementArr = document.getElementsByClassName("showBigImage");
  if (elementArr.length == 0) {
  this.createShowImage();
  }
  for (let y = 0; y < this.opPicsList.length; y++) {
  if (name == this.opPicsList[y].name) {
   document.getElementById("bigImageE").src = this.opPicsList[y].url;
   break;
  }
  }
 },
 closeImageShow() {
  let elementArr = document.getElementsByClassName("showBigImage");
  let main = document.getElementsByClassName("main");
  let count = elementArr.length;
  for (let i = 0; i < count; i++) {
  main[0].removeChild(elementArr[0]);
  }
 },
 rotateImage() {
  let imageE = document.getElementById("bigImageE");
  this.currentRotate = (this.currentRotate + 90) % 360;
  imageE.style.transform =
  imageE.style.transform.split(" ")[0] +
  " rotate(" +
  this.currentRotate +
  "deg)";
 },
 toLeftImage() {
  for (let y = 0; y < this.opPicsList.length; y++) {
  if (this.currentImageName == this.opPicsList[y].name) {
   if (y - 1 < 0) {
   this.$Message.info("已经是最左边的一张图了哦~");
   } else {
   this.currentImageName = this.opPicsList[y - 1].name;
   let imageE = document.getElementById("bigImageE");
   imageE.src = this.opPicsList[y - 1].url;
   // 加载完成执行
   imageE.onload = function() {
    if (imageE.naturalHeight < window.innerHeight) {
    //图片高度小于屏幕则需要垂直居中处理
    imageE.style.height = imageE.naturalHeight + "px";
    imageE.style.top = "50%";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(-50%)";
    imageE.style.zoom = "100%";
    } else {
    //需要去除前一张图片的效果
    imageE.style.height = window.innerHeight + "px";
    imageE.style.top = "0";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(0%)";
    imageE.style.zoom = "100%";
    }
   };
   }
   break;
  }
  }
 },
 toRightImage() {
  for (let y = 0; y < this.opPicsList.length; y++) {
  if (this.currentImageName == this.opPicsList[y].name) {
   if (y + 1 == this.opPicsList.length) {
   this.$Message.info("已经是最右边的一张图了哦~");
   } else {
   this.currentImageName = this.opPicsList[y + 1].name;
   let imageE = document.getElementById("bigImageE");
   imageE.src = this.opPicsList[y + 1].url;
   // 加载完成执行
   imageE.onload = function() {
    if (imageE.naturalHeight < window.innerHeight) {
    //图片高度小于屏幕则需要垂直居中处理
    imageE.style.height = imageE.naturalHeight + "px";
    imageE.style.top = "50%";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(-50%)";
    imageE.style.zoom = "100%";
    } else {
    //需要去除前一张图片的效果
    imageE.style.height = window.innerHeight + "px";
    imageE.style.top = "0";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(0%)";
    imageE.style.zoom = "100%";
    }
   };
   }
   break;
  }
  }
 },
 createShowImage() {
  //创建图片显示
  let main = document.getElementsByClassName("main");
  let topContainer = document.createElement("div");
  let scrollContainer = document.createElement("div");
  topContainer.style.position = "fixed";
  topContainer.style.zIndex = "80";
  topContainer.style.background = "rgba(0,0,0,0.80)";
  topContainer.style.height = "100%";
  topContainer.style.width = "100%";
  topContainer.style.textAlign = "center";
  topContainer.className = "showBigImage";
 
  let imageContainer = document.createElement("div");
  imageContainer.style.width = window.innerWidth + "px";
  imageContainer.style.height = window.innerHeight + "px";
  imageContainer.style.margin = "0 auto";
  imageContainer.style.overflow = "auto";
  imageContainer.style.top = "50%";
  imageContainer.style.position = "relative";
  imageContainer.style.transform = "translateY(-50%)";
 
  let imageE = document.createElement("img");
  imageE.src = iconNoImages;
  imageE.title = "鼠标滚轮滚动可缩放图片";
  imageE.id = "bigImageE";
  // 加载完成执行
  imageE.onload = function() {
  if (imageE.naturalHeight < window.innerHeight) {
   //图片高度小于屏幕则需要垂直居中处理
   // imageE.style.width = "100%";
   imageE.style.top = "50%";
   imageE.style.position = "relative";
   imageE.style.transform = "translateY(-50%)";
  } else {
   imageE.style.height = window.innerHeight + "px";
  }
  };
 
  this.bigImage = imageE;
 
  //添加鼠标滚轮事件缩放图片
  if (imageE.addEventListener) {
  // IE9, Chrome, Safari, Opera
  imageE.addEventListener("mousewheel", this.rollImg, false);
  // Firefox
  imageE.addEventListener("DOMMouseScroll", this.rollImg, false);
  } else {
  // IE 6/7/8
  imageE.attachEvent("onmousewheel", this.rollImg);
  }
  imageContainer.appendChild(imageE);
  topContainer.appendChild(imageContainer);
 
  main[0].appendChild(topContainer);
 
  //创建点击左右浏览按钮
  //左按钮
  let imgLeft = document.createElement("img");
  imgLeft.src = iconLeft;
  imgLeft.style.zIndex = "101";
  imgLeft.style.position = "fixed";
  imgLeft.style.top = "50%";
  imgLeft.style.transform = "translateY(-50%)";
  imgLeft.style.left = "12%";
  imgLeft.style.cursor = "pointer";
  imgLeft.className = "showBigImage";
  //添加鼠标点击事件切换图片
  imgLeft.addEventListener("click", this.toLeftImage);
  //右按钮
  let imgRight = document.createElement("img");
  imgRight.src = iconRight;
  imgRight.style.zIndex = "101";
  imgRight.style.position = "fixed";
  imgRight.style.top = "50%";
  imgRight.style.transform = "translateY(-50%)";
  imgRight.style.right = "12%";
  imgRight.style.cursor = "pointer";
  imgRight.className = "showBigImage";
  //添加鼠标点击事件切换图片
  imgRight.addEventListener("click", this.toRightImage);
 
  //大图片选转
  let imgRotate = document.createElement("img");
  imgRotate.id = "rotateImageBtn";
  imgRotate.src = iconRotate;
  imgRotate.style.zIndex = "102";
  imgRotate.style.position = "fixed";
  imgRotate.style.top = "5%";
  imgRotate.style.right = "5%";
  imgRotate.style.transform = "translateY(-50%)";
  imgRotate.style.cursor = "pointer";
  imgRotate.className = "showBigImage";
  //添加鼠标点击事件旋转大图
  imgRotate.addEventListener("click", this.rotateImage);
 
  //关闭按钮
  let imgClose = document.createElement("img");
  imgClose.src = iconClose;
  imgClose.style.zIndex = "101";
  imgClose.style.position = "fixed";
  imgClose.style.top = "5%";
  imgClose.style.right = "1%";
  imgClose.style.transform = "translateY(-50%)";
  imgClose.style.cursor = "pointer";
  imgClose.className = "showBigImage";
  //添加鼠标点击事件关闭显示大图
  imgClose.addEventListener("click", this.closeImageShow);
 
  main[0].appendChild(imgLeft);
  main[0].appendChild(imgRight);
  main[0].appendChild(imgClose);
  main[0].appendChild(imgRotate);
 
 }
 },
 mounted() {
 this.loadDetail();
 }
};
</script>

可以看到,这个图片大图预览是用js创建的,而且是在main元素下添加的元素。因为这个是在ivew-admin框架下写的,其主要内容区的z-index是比菜单和header小的,所以如果在内容去写这个图片全局预览阴影区域无法覆盖整个页面。所以需要在main下加入元素。

组件方式:

<template>
 <div>
 </div>
</template>
<script>
import iconLeft from "@/images/icon-left.png";
import iconRight from "@/images/icon-right.png";
import iconClose from "@/images/icon-close.png";
import iconRotate from "@/images/icon-rotate.png";
import iconNoImages from "@/images/icon-no-images.png";
import {IMAGE_URL_PREFIX} from "@/config/constant";
export default {
 data() {
 return {
  opPicsList: [
  {
   name: "none",
   url: iconNoImages
  }
  ],
  imgName: "",
  bigImage: null,
  currentImageName: "",
  currentRotate: 0
 };
 },
 props: {
 },
 methods: {
 loadImages(opPics) {
  this.opPicsList.splice(0, this.opPicsList.length);
  opPics.split(",").forEach((element, index) => {
  this.opPicsList.push({
   name: index,
   url: IMAGE_URL_PREFIX + element
  });
  });
  this.handleView("0");
 },
 rollImg() {
  /* 获取当前页面的缩放比
   若未设置zoom缩放比,则为默认100%,即1,原图大小
  */
 
  var zoom = parseInt(this.bigImage.style.zoom) || 100;
  /* event.wheelDelta 获取滚轮滚动值并将滚动值叠加给缩放比zoom
   wheelDelta统一为±120,其中正数表示为向上滚动,负数表示向下滚动
  */
  zoom += event.wheelDelta / 12;
  /* 如果缩放比大于0,则将缩放比加载到页面元素 */
  if (zoom >= 100) {
  this.bigImage.style.zoom = zoom + "%";
  }
  /* 如果缩放比不大于0,则返回false,不执行操作 */
  return false;
 },
 handleView(name) {
  if (this.opPicsList[0].name == "none") {
  this.$Message.error("没有图片哦~");
  return;
  }
  this.currentImageName = name;
 
  let elementArr = document.getElementsByClassName("showBigImage");
  if (elementArr.length == 0) {
  this.createShowImage();
  }
  for (let y = 0; y < this.opPicsList.length; y++) {
  if (name == this.opPicsList[y].name) {
   document.getElementById("bigImageE").src = this.opPicsList[y].url;
   // debugger
   // document.getElementById("bigImageE").width = this.opPicsList[y].url;
   // document.getElementById("bigImageE").height = this.opPicsList[y].url;
   // for (let i = 0; i < elementArr.length; i++) {
   // elementArr[i].style.display = "block";
   // }
   break;
  }
  }
 },
 closeImageShow() {
  let elementArr = document.getElementsByClassName("showBigImage");
  let main = document.getElementsByClassName("main");
  let count = elementArr.length;
  for (let i = 0; i < count; i++) {
  main[0].removeChild(elementArr[0]);
  }
 },
 rotateImage() {
  let imageE = document.getElementById("bigImageE");
  this.currentRotate = (this.currentRotate + 90) % 360;
  imageE.style.transform =
  imageE.style.transform.split(" ")[0] +
  " rotate(" +
  this.currentRotate +
  "deg)";
 },
 toLeftImage() {
  for (let y = 0; y < this.opPicsList.length; y++) {
  if (this.currentImageName == this.opPicsList[y].name) {
   if (y - 1 < 0) {
   this.$Message.info("已经是最左边的一张图了哦~");
   } else {
   this.currentImageName = this.opPicsList[y - 1].name;
   let imageE = document.getElementById("bigImageE");
   imageE.src = this.opPicsList[y - 1].url;
   // 加载完成执行
   imageE.onload = function() {
    if (imageE.naturalHeight < window.innerHeight) {
    //图片高度小于屏幕则需要垂直居中处理
    imageE.style.height = imageE.naturalHeight + "px";
    imageE.style.top = "50%";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(-50%)";
    imageE.style.zoom = "100%";
    } else {
    //需要去除前一张图片的效果
    imageE.style.height = window.innerHeight + "px";
    imageE.style.top = "0";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(0%)";
    imageE.style.zoom = "100%";
    }
   };
   }
   break;
  }
  }
 },
 toRightImage() {
  for (let y = 0; y < this.opPicsList.length; y++) {
  if (this.currentImageName == this.opPicsList[y].name) {
   if (y + 1 == this.opPicsList.length) {
   this.$Message.info("已经是最右边的一张图了哦~");
   } else {
   this.currentImageName = this.opPicsList[y + 1].name;
   let imageE = document.getElementById("bigImageE");
   imageE.src = this.opPicsList[y + 1].url;
   // 加载完成执行
   imageE.onload = function() {
    if (imageE.naturalHeight < window.innerHeight) {
    //图片高度小于屏幕则需要垂直居中处理
    imageE.style.height = imageE.naturalHeight + "px";
    imageE.style.top = "50%";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(-50%)";
    imageE.style.zoom = "100%";
    } else {
    //需要去除前一张图片的效果
    imageE.style.height = window.innerHeight + "px";
    imageE.style.top = "0";
    imageE.style.position = "relative";
    imageE.style.transform = "translateY(0%)";
    imageE.style.zoom = "100%";
    }
   };
   }
   break;
  }
  }
 },
 createShowImage() {
  //创建图片显示
  // let elementArr = document.getElementsByClassName("showBigImage");
  // if (elementArr.length == 0) {
  let main = document.getElementsByClassName("main");
  let topContainer = document.createElement("div");
  let scrollContainer = document.createElement("div");
  topContainer.style.position = "fixed";
  topContainer.style.zIndex = "80";
  topContainer.style.background = "rgba(0,0,0,0.80)";
  topContainer.style.height = "100%";
  topContainer.style.width = "100%";
  topContainer.style.textAlign = "center";
  topContainer.className = "showBigImage";
  // topContainer.style.display = "none";
 
  let imageContainer = document.createElement("div");
  imageContainer.style.width = window.innerWidth + "px";
  imageContainer.style.height = window.innerHeight + "px";
  imageContainer.style.margin = "0 auto";
  imageContainer.style.overflow = "auto";
  imageContainer.style.top = "50%";
  imageContainer.style.position = "relative";
  imageContainer.style.transform = "translateY(-50%)";
 
  let imageE = document.createElement("img");
  imageE.src = iconNoImages;
  imageE.title = "鼠标滚轮滚动可缩放图片";
  imageE.id = "bigImageE";
  // 加载完成执行
  imageE.onload = function() {
  if (imageE.naturalHeight < window.innerHeight) {
   //图片高度小于屏幕则需要垂直居中处理
   // imageE.style.width = "100%";
   imageE.style.top = "50%";
   imageE.style.position = "relative";
   imageE.style.transform = "translateY(-50%)";
  } else {
   imageE.style.height = window.innerHeight + "px";
  }
  };
  // imageE.style.width = "100%";
  // imageE.style.width = "475px";
  // imageE.style.height = window.innerHeight + 'px';
 
  // imageE.style.objectFit= "scale-down";
  // imageE.style.height = "100%";
  // imageE.style.top = "50%";
  // imageE.style.position = "relative";
  // imageE.style.transform = "translateY(-50%)";
  this.bigImage = imageE;
 
  //添加鼠标滚轮事件缩放图片
  if (imageE.addEventListener) {
  // IE9, Chrome, Safari, Opera
  imageE.addEventListener("mousewheel", this.rollImg, false);
  // Firefox
  imageE.addEventListener("DOMMouseScroll", this.rollImg, false);
  } else {
  // IE 6/7/8
  imageE.attachEvent("onmousewheel", this.rollImg);
  }
  imageContainer.appendChild(imageE);
  topContainer.appendChild(imageContainer);
 
  main[0].appendChild(topContainer);
 
  //创建点击左右浏览按钮
  //左按钮
  let imgLeft = document.createElement("img");
  imgLeft.src = iconLeft;
  imgLeft.style.zIndex = "101";
  imgLeft.style.position = "fixed";
  imgLeft.style.top = "50%";
  imgLeft.style.transform = "translateY(-50%)";
  imgLeft.style.left = "12%";
  imgLeft.style.cursor = "pointer";
  imgLeft.className = "showBigImage";
  //添加鼠标点击事件切换图片
  imgLeft.addEventListener("click", this.toLeftImage);
  //右按钮
  let imgRight = document.createElement("img");
  imgRight.src = iconRight;
  imgRight.style.zIndex = "101";
  imgRight.style.position = "fixed";
  imgRight.style.top = "50%";
  imgRight.style.transform = "translateY(-50%)";
  imgRight.style.right = "12%";
  imgRight.style.cursor = "pointer";
  imgRight.className = "showBigImage";
  //添加鼠标点击事件切换图片
  imgRight.addEventListener("click", this.toRightImage);
 
  //大图片选转
  let imgRotate = document.createElement("img");
  imgRotate.id = "rotateImageBtn";
  imgRotate.src = iconRotate;
  imgRotate.style.zIndex = "102";
  imgRotate.style.position = "fixed";
  imgRotate.style.top = "5%";
  imgRotate.style.right = "5%";
  imgRotate.style.transform = "translateY(-50%)";
  imgRotate.style.cursor = "pointer";
  imgRotate.className = "showBigImage";
  //添加鼠标点击事件旋转大图
  imgRotate.addEventListener("click", this.rotateImage);
 
  //关闭按钮
  let imgClose = document.createElement("img");
  imgClose.src = iconClose;
  imgClose.style.zIndex = "101";
  imgClose.style.position = "fixed";
  imgClose.style.top = "5%";
  imgClose.style.right = "1%";
  imgClose.style.transform = "translateY(-50%)";
  imgClose.style.cursor = "pointer";
  imgClose.className = "showBigImage";
  //添加鼠标点击事件关闭显示大图
  imgClose.addEventListener("click", this.closeImageShow);
 
  // imgLeft.style.display = "none";
  // imgRight.style.display = "none";
  // imgClose.style.display = "none";
  main[0].appendChild(imgLeft);
  main[0].appendChild(imgRight);
  main[0].appendChild(imgClose);
  main[0].appendChild(imgRotate);
  // main[0].style.textAlign = "center";
  // this.imgName = name;
  // this.visible = true;
  // }
 }
 },
 mounted() {
 // this.loadImages();
 }
};
</script>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
js限制文本框输入长度两种限制方式(长度、字节数)
Dec 19 Javascript
JavaScript 上万关键字瞬间匹配实现代码
Jul 07 Javascript
在服务端(Page.Write)调用自定义的JS方法详解
Aug 09 Javascript
浅析Node在构建超媒体API中的作用
Jul 30 Javascript
js实现精美的银灰色竖排折叠菜单
May 16 Javascript
CSS图片响应式 垂直水平居中
Aug 14 Javascript
javascript的几种继承方法介绍
Mar 22 Javascript
javascript的函数劫持浅析
Sep 26 Javascript
微信小程序实现点击按钮修改字体颜色功能【附demo源码下载】
Dec 05 Javascript
深入浅析var,let,const的异同点
Aug 07 Javascript
JS编写兼容IE6,7,8浏览器无缝自动轮播
Oct 12 Javascript
详解vue中使用transition和animation的实例代码
Dec 12 Vue.js
vue实现图片预览组件封装与使用
Jul 13 #Javascript
微信小程序基于Taro的分享图片功能实践详解
Jul 12 #Javascript
小程序实现悬浮搜索框
Jul 12 #Javascript
小程序实现搜索框功能
Mar 26 #Javascript
iview的table组件自带的过滤器实现
Jul 12 #Javascript
es6中比较有用的7个技巧小结
Jul 12 #Javascript
echarts大屏字体自适应的方法步骤
Jul 12 #Javascript
You might like
php版微信公众平台回复中文出现乱码问题的解决方法
2016/09/22 PHP
php设计模式之中介者模式分析【星际争霸游戏案例】
2020/03/23 PHP
Javascript中的window.event.keyCode使用介绍
2011/04/26 Javascript
JQuery1.6 使用方法三
2011/11/23 Javascript
javascript怎么禁用浏览器后退按钮
2014/03/27 Javascript
Jquery图片延迟加载插件jquery.lazyload.js的使用方法
2014/05/21 Javascript
探讨js字符串数组拼接的性能问题
2014/10/11 Javascript
AngularJS 使用$sce控制代码安全检查
2016/01/05 Javascript
javascript基本算法汇总
2016/03/09 Javascript
JavaScript常用字符串与数组扩展函数小结
2016/04/24 Javascript
Bootstrap组件学习之导航、标签、面包屑导航(精品)
2016/05/17 Javascript
Actionscript与javascript交互实例程序(修改)
2016/09/22 Javascript
纯js实现手风琴效果代码
2020/04/17 Javascript
ajax实现动态下拉框示例
2017/01/10 Javascript
bootstrap配合Masonry插件实现瀑布式布局
2017/01/18 Javascript
Vue2组件tree实现无限级树形菜单
2017/03/29 Javascript
页面缩放兼容性处理方法(zoom,Firefox火狐浏览器)
2017/08/29 Javascript
使用vuex缓存数据并优化自己的vuex-cache
2018/05/30 Javascript
webpack4实现不同的导出类型
2019/04/09 Javascript
[57:09]DOTA2-DPC中国联赛 正赛 Phoenix vs Dynasty BO3 第一场 1月26日
2021/03/11 DOTA
python里对list中的整数求平均并排序
2014/09/12 Python
Python的动态重新封装的教程
2015/04/11 Python
python关闭windows进程的方法
2015/04/18 Python
python如何通过protobuf实现rpc
2016/03/06 Python
Python3中的列表,元组,字典,字符串相关知识小结
2017/11/10 Python
Django框架实现的分页demo示例
2019/05/25 Python
python实现屏保程序(适用于背单词)
2019/07/30 Python
在pycharm中实现删除bookmark
2020/02/14 Python
python实现密码强度校验
2020/03/18 Python
详解python中GPU版本的opencv常用方法介绍
2020/07/24 Python
Python通过Schema实现数据验证方式
2020/11/12 Python
日本最大的彩色隐形眼镜销售网站:CharmColor
2020/09/09 全球购物
药学专业个人的自我评价
2013/12/31 职场文书
能源工程专业应届生求职信
2014/03/01 职场文书
申请吧主发表的感言
2015/08/03 职场文书
Win11如何查看显卡型号 Win11查看显卡型号的方法
2022/08/14 数码科技