一个神奇的交叉观察 API Intersection Observer

发表于 3年以前  | 总阅读数:305 次

前言

前端开发肯定离不开判断一个元素是否能被用户看见,然后再基于此进行一些交互。

过去,要检测一个元素是否可见或者两个元素是否相交并不容易,很多解决办法不可靠或性能很差。然而,随着互联网的发展,这种需求却与日俱增,比如,下面这些情况都需要用到相交检测:

  • 图片懒加载——当图片滚动到可见时才进行加载
  • 内容无限滚动——也就是用户滚动到接近内容底部时直接加载更多,而无需用户操作翻页,给用户一种网页可以无限滚动的错觉
  • 检测广告的曝光情况——为了计算广告收益,需要知道广告元素的曝光情况
  • 在用户看见某个区域时执行任务或播放动画

过去,相交检测通常要用到事件监听,并且需要频繁调用 Element.getBoundingClientRect() 方法以获取相关元素的边界信息。事件监听和调用 Element.getBoundingClientRect() 都是在主线程上运行,因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅。

上面这一段话来自 MDN ,中心思想就是现在判断一个元素是否能被用户看见的使用场景越来越多,监听 scroll 事件以及通过 Element.getBoundingClientRect() 获取节点位置的方式,又麻烦又不好用,那么怎么办呢。于是就有了今天的内容 Intersection Observer API

Intersection Observer API 是什么

我们需要观察的元素被称为 目标元素(target),设备视窗或者其他指定的元素视口的边界框我们称它为 根元素(root),或者简称为

Intersection Observer API 翻译过来叫做 “交叉观察器”,因为判断元素是否可见(通常情况下)的本质就是判断目标元素和根元素是不是产生了 交叉区域

为什么是通常情况下,因为当我们 css 设置了 opacity: 0visibility: hidden 或者 用其他的元素覆盖目标元素 的时候,对于视图来说是不可见的,但对于交叉观察器来说是可见的。这里可能有点抽象,大家只需记住,交叉观察器只关心 目标元素根元素 是否有 交叉区域, 而不管视觉上能不能看见这个元素。当然如果设置了 display:none,那么交叉观察器就不会生效了,其实也很好理解,因为元素已经不存在了,那么也就监测不到了。

一句话总结:Intersection Observer API 提供了一种异步检测目标元素与祖先元素或 viewport 相交情况变化的方法。 -- MDN

现在不懂没关系,咱们接着往下看,看完自然就明白了。

Intersection Observer API 怎么玩

生成观察器

// 调用构造函数 IntersectionObserver 生成观察器
const myObserver = new IntersectionObserver(callback, options);  
复制代码

首先调用浏览器原生构造函数 IntersectionObserver ,构造函数的返回值是一个 观察器实例

构造函数 IntersectionObserver 接收两个参数

  • callback: 可见性发生变化时触发的回调函数
  • options: 配置对象(可选,不传时会使用默认配置)

构造函数接收的参数 options

为了方便理解,我们先看第二个参数 options 。一个可以用来配置观察器实例的对象,那么这个配置对象都包含哪些属性呢?

  • root: 设置目标元素的根元素,也就是我们用来判断元素是否可见的区域,必须是目标元素的父级元素,如果不指定的话,则使用浏览器视窗,也就是 document
  • rootMargin: 一个在计算交叉值时添加至根的边界中的一组偏移量,类型为字符串 (string) ,可以有效的缩小或扩大根的判定范围从而满足计算需要。语法大致和CSS 中 margin 属性等同,默认值 “0px 0px 0px 0px” ,如果有指定 root 参数,则 rootMargin 也可以使用百分比来取值。
  • threshold: 介于 01 之间的数字,指示触发前应可见的百分比。也可以是一个数字数组,以创建多个触发点,也被称之为 阈值。如果构造器未传入值, 则默认值为 0
  • trackVisibility: 一个布尔值,指示当前观察器是否将跟踪目标可见性的更改,默认为 false ,注意,此处的可见性并非指目标元素和根元素是否相交,而是指视图上是否可见,这个我们之前就已经分析过了,如果此值设置为 false 或不设置,那么回调函数参数中 IntersectionObserverEntryisVisible 属性将永远返回 false
  • delay: 一个数字,也就是回调函数执行的延迟时间(毫秒)。如果 trackVisibility 设置为 true,则此值必须至少设置为 100 ,否则会报错(但是这里我也只是亲测出来的,并不知道为什么会设计成这样,如果有大佬了解请指教一下)。

构造函数接收的参数 callback

当元素可见比例超过指定阈值后,会调用一个回调函数,此回调函数接受两个参数:存放 IntersectionObserverEntry 对象的数组和观察器实例(可选)。

((doc) => {
  //回调函数
  const callback = (entries, observer) => {
    console.log('~ 执行了一次callback');
    console.log('~ entries:', entries);
    console.log('~ observer:', observer);
  };
  //配置对象
  const options = {};
  //创建观察器
  const myObserver = new IntersectionObserver(callback, options);
  //获取目标元素
  const target = doc.querySelector(".target")
  //开始监听目标元素
  myObserver.observe(target);
})(document)

我们把这两个参数打印出来看一下,可以看到,第一个参数是一个数组,每一项都是一个目标元素对应的 IntersectionObserverEntry对象,第二个参数是观察器实例对象 IntersectionObserver

什么是 IntersectionObserverEntry 对象

展开 IntersectionObserverEntry 看一下都有什么。

  • boundingClientRect: 一个对象,包含目标元素的 getBoundingClientRect() 方法的返回值。
  • intersectionRatio: 一个对象,包含目标元素与根元素交叉区域 getBoundingClientRect() 的返回值。
  • intersectionRect: 目标元素的可见比例,即 intersectionRectboundingClientRect 的比例,完全可见时为 1 ,完全不可见时小于等于 0
  • isIntersecting: 返回一个布尔值,如果目标元素与根元素相交,则返回 true ,如果 isIntersectingtrue,则 target 元素至少已经达到 thresholds 属性值当中规定的其中一个阈值,如果是 falsetarget 元素不在给定的阈值范围内可见。
  • isVisible: 这个看字面意思应该是 “是否可见” ,如果要让这个属性生效,那么在使用构造函数生成观察器实例的时候,传入的 options 参数必须配置 trackVisibilitytrue,并且 delay 设置为大于 100 ,否则该属性将永远返回 false
  • rootBounds: 一个对象,包含根元素的 getBoundingClientRect() 方法的返回值。
  • target:: 被观察的目标元素,是一个 DOM 节点。在观察者包含多个目标的情况下,这是确定哪个目标元素触发了此相交更改的简便方法。
  • time: 该属性提供从 首次创建观察者触发此交集改变 的时间(以毫秒为单位)。通过这种方式,你可以跟踪观察器达到特定阈值所花费的时间。即使稍后将目标再次滚动到视图中,此属性也会提供新的时间。这可用于跟踪目标元素进入和离开根元素的时间,以及两个阈值触发的间隔时间。

这里再看一下 boundingClientRectintersectionRatiorootBounds 三个属性展开的内容都有什么。

  • bottom: 元素下边距离页面上边的距离
  • left: 元素左边距离页面左边的距离
  • right: 元素右边距离页面左边的距离
  • top: 元素上边距离页面上边的距离
  • width: 元素的宽
  • height: 元素的高
  • x: 等同于 left,元素左边距离页面左边的距离
  • y: 等同于 top,元素上边距离页面上边的距离

用一张图来展示一下这几个属性,特别需要注意的是 rightbottom ,跟我们平时写 cssposition 那个不一样 。

那么第二个参数 IntersectionObserver 观察器实例对象都有什么呢

别着急,接着往下看,实例属性部分。

观察器实例属性

上面留了一个坑,回调函数的第二个参数 IntersectionObserver 观察器实例对象都有什么呢?我们把实例对象打印出来看一下

((doc) => {
  //回调函数
  const callback = () => {};
  //配置对象
  const options = {};
  //创建观察器
  const myObserver = new IntersectionObserver(callback, options);
  //获取目标元素
  const target = doc.querySelector(".target")
  //开始监听目标元素
  myObserver.observe(target);
  console.log('~ myObserver:', myObserver);
})(document)

可以看到,我们的观察器实例上面包含如下属性

  • root
  • rootMargin
  • thresholds
  • trackVisibility
  • delay

是不是特别眼熟,没错,就是我们创建观察者实例的时候,传入的 options 对象,只不过 options 对象是可选的,观察器实例的属性就使用我们传入的 options 对象,如果没传就使用默认值,唯一不同的是,options 中 的属性 threshold 是单数,而我们实例获取到的 thresholds 是复数。

值得注意的是,这里的所有属性都是 只读 的,也就是说一旦观察器被创建,则 无法 更改其配置,所以一个给定的观察者对象只能用来监听可见区域的特定变化值。

接下来我们就通过代码结合动图演示一下这些属性

((doc) => {
  let n = 0
  //获取目标元素
  const target = doc.querySelector(".target")
  //获取根元素
  const root = doc.querySelector(".out-container")
  //回调函数
  const callback = (entries, observer) => {
    n++
    console.log(`~ 执行了 ${n} 次callback`);
    console.log('~ entries:', entries);
    console.log('~ observer:', observer);
  };
  //配置对象
  const options = {
    root: root,
    rootMargin: '0px 0px 0px 0px',
    threshold: [0.5],
    trackVisibility: true,
    delay: 100
  };
  //创建观察器
  const myObserver = new IntersectionObserver(callback, options);
  //开始监听目标元素
  myObserver.observe(target);
  console.log('~ myObserver:', myObserver);
})(document)

root这个没什么说的,就是设置指定节点为根元素 rootMargin我们把 rootMargin 修改为 '50px 50px 50px 50px',可以看到,我们的目标元素还没有露出来的时候回调函数就已经执行了,也就是说目标元素距离根元素还有 50pxmargin 时,观察器就认为是发生了交叉。 thresholds我们把 threshold 修改为 [0.1, 0.3, 0.5, 0.8, 1],可以看到,回调函数触发了多次,也就是说当交叉区域的百分比,每达到指定的阈值时都会触发一次回调函数。

注意 Intersection Observer API 无法提供重叠的像素个数或者具体哪个像素重叠,他的更常见的使用方式是——当两个元素相交比例在 N% 左右时,触发回调,以执行某些逻辑。 -- MDN

trackVisibility修改 trackVisibilitytrue ,可以看到, isVisible 属性值为 true 修改 css 属性 为 opacity: 0,可以看到,虽然我们蓝色小方块并没有出现在视图中,但是回调函数已经执行了,并且 isVisible 属性值为 falseisIntersecting 值为 true delay回调函数延迟触发,我们修改 delay3000,可以看到 log3000ms 以后才输出的。

观察器实例方法

通过此段代码来演示观察器实例方法,为了方便演示,我添加了几个对应的按钮。

((doc) => {
  let n = 0
  //获取目标元素
  const target1 = doc.querySelector(".target1")
  const target2 = doc.querySelector(".target2")
  //添加几个按钮方便操作
  const observe = doc.querySelector(".observe")
  const unobserve = doc.querySelector(".unobserve")
  const disconnect = doc.querySelector(".disconnect")
  observe.addEventListener('click', () => myObserver.observe(target1))
  unobserve.addEventListener('click', () => myObserver.unobserve(target1))
  disconnect.addEventListener('click', () => myObserver.disconnect())
  //获取根元素
  const root = doc.querySelector(".out-container")
  //回调函数
  const callback = (entries, observer) => {
    n++
    console.log(`~ 执行了 ${n} 次callback`);
    console.log('~ entries:', entries);
    console.log('~ observer:', observer);
  };
  //配置对象
  const options = {
    root: root,
    rootMargin: '0px 0px 0px 0px',
    threshold: [0.1, 0.2, 0.3, 0.5],
    trackVisibility: true,
    delay: 100
  };
  //创建观察器
  const myObserver = new IntersectionObserver(callback, options);
  //开始监听目标元素
  myObserver.observe(target2);
  console.log('~ myObserver:', myObserver);
})(document)

observe

 const myObserver = new IntersectionObserver(callback, options);
 myObserver.observe(target);

接受一个目标元素作为参数。很好理解,当我们创建完观察器实例后,要手动的调用 observe 方法来通知它开始监测目标元素。

可以在同一个观察者对象中配置监听多个目标元素

target2 元素是通过代码自动监测的,而 target1 则是我们在点击了 observe 按钮之后开始监测的。通过动图可以看到,当我单击 observe 按钮后,我们的 entries 数组里面就包含了两条数据,前文中说到,可以通过 target 属性来判断是哪个目标元素。

unobserve

 const myObserver = new IntersectionObserver(callback, options);
 myObserver.observe(target);
 myObserver.unobserve(target)
复制代码

接收一个目标元素作为参数,当我们不想监听某个元素的时候,需要手动调用 unobserve 方法来停止监听指定目标元素。通过动图可以发现,当我们点击 unobserve 按钮后,由两条数据变成了一条数据,说明 target1 已经不再接受监测了。

disconnect

 const myObserver = new IntersectionObserver(callback, options);
 myObserver.disconnect()

当我们不想监测任何一个目标元素时,我们需要手动调用 disconnect 方法停止监听工作。通过动图可以看到,当我们点击 disconnect 按钮后,控制台不再输出 log ,说明监听工作已经停止,可以通过 observe 再次开启监听工作。

takeRecords

返回所有观察目标的 IntersectionObserverEntry 对象数组,应用场景较少。

当观察到交互动作发生时,回调函数并不会立即执行,而是在空闲时期使用 requestIdleCallback 来异步执行回调函数,但是也提供了同步调用的 takeRecords 方法。

如果异步的回调先执行了,那么当我们调用同步的 takeRecords 方法时会返回空数组。同理,如果已经通过 takeRecords 获取了所有的观察者实例,那么回调函数就不会被执行了。

注意事项

构造函数 IntersectionObserver 配置的回调函数都在哪些情况下被调用?

构造函数 IntersectionObserver 配置的回调函数,在以下情况发生时可能会被调用

  • 当目标(target)元素与根(root)元素发生交集的时候执行。
  • 两个元素的相交部分大小发生变化时。
  • Observer 第一次监听目标元素的时候。
((doc) => {
  //回调函数
  const callback = () => {
    console.log('~ 执行了一次callback');
  };
  //配置对象
  const options = {};
  //观察器实例
  const myObserver = new IntersectionObserver(callback, options);
  //目标元素
  const target = doc.querySelector("#target")
  //开始观察
  myObserver.observe(target);
})(document)

可以看到,无论目标元素是否与根元素相交,当我们第一次监听目标元素的时候,回调函数都会触发一次,所以不要直接在回调函数里写逻辑代码,尽量通过 isIntersecting 或者 intersectionRect 进行判断之后再执行逻辑代码。

页面的可见性如何监测

页面的可见性可以通过document.visibilityState或者document.hidden获得。页面可见性的变化可以通过document.visibilitychange来监听。

可见性和交叉观察

css 设置了opacity: 0visibility: hidden 以及 用其他的元素覆盖目标元素 ,都不会影响交叉观察器的监测,也就是都不会影响 isIntersecting 属性的结果,但是会影响 isVisible 属性的结果, 如果元素设置了 display:none 就不会被检测了。当然影响元素可视性的属性不止上述这些,还包括positionmarginclip 等等等等...就靠小伙伴们自行发掘了

交集的计算

所有区域均被 Intersection Observer API 当做一个 矩形 看待。如果元素是不规则的图形也将会被看成一个包含元素所有区域的最小矩形,相似的,如果元素发生的交集部分不是一个矩形,那么也会被看作是一个包含他所有交集区域的最小矩形。

我怎么知道目标元素来自视口的上方还是下方

目标元素滚动的方向也是可以判断的,原理是根元素的 entry.rootBounds.y 是固定不变的 ,所以我们只需要计算 entry.boundingClientRect.yentry.rootBounds.y 的大小,当回调函数触发的时候,我们记录下当时的位置,如果 entry.boundingClientRect.y < entry.rootBounds.y,说明是在视口下方,那么当下一次目标元素可见的时候,我们就知道目标元素时来自视口下方的,反之亦然。

let wasAbove = false;
function callback(entries, observer) {
    entries.forEach(entry => {
        const isAbove = entry.boundingClientRect.y < entry.rootBounds.y;
        if (entry.isIntersecting) {
            if (wasAbove) {
                // Comes from top
            }
        }
        wasAbove = isAbove;
    });
}

应用场景

介绍完基础知识,总得来几个实例(演示代码采用VUE3.0),当然实际场景要比这复杂的多,如何在自己的工作学习中应用,还是要靠小伙伴们多多开动聪明的大脑~

数据列表无限滚动

<template>
  <div class="box">
    <div class="vbody"
         v-for='item in list'
         :key='item'>内容区域{{item}}</div>
    <div class="reference"
         ref='reference'></div>
  </div>
</template>

<script lang='ts'>
import { defineComponent, onMounted, reactive, ref } from 'vue'

export default defineComponent({
  name: '',
  setup() {
    const reference = ref(null)
    const list = reactive([1, 2, 3, 4, 5, 6, 7, 8, 9, 10])
    onMounted(() => {
      let n = 10
      //回调函数
      const callback = (entries) => {
        const myEntry = entries[0]
        if (myEntry.isIntersecting) {
          console.log(`~ 触发了无线滚动,开始模拟请求数据 ${n}`)
          n++
          list.push(n)
        }
      }
      //配置对象
      const options = {
        root: null,
        rootMargin: '0px 0px 0px 0px',
        threshold: [0, 1],
        trackVisibility: true,
        delay: 100,
      }
      //观察器实例
      const myObserver = new IntersectionObserver(callback, options)
      //开始观察
      myObserver.observe(reference.value)
    })

    return { reference, list }
  },
})
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.reference {
  width: 100%;
  visibility: hidden;
}
.vbody {
  width: 100%;
  height: 200px;
  background-color: red;
  color: aliceblue;
  font-size: 40px;
  text-align: center;
  line-height: 200px;
  margin: 10px 0;
}
</style>

我们只需要在底部添加一个参考元素,当参考元素可见时,就向后台请求数据,就可以实现无线滚动的效果了。

图片预加载

<template>
  <div class="box">
    <div class="vbody">内容区域</div>
    <div class="vbody">内容区域</div>
    <div class="header"
         ref='header'>
      <img :src="url">
    </div>
    <div class="vbody">内容区域</div>
  </div>
</template>

<script lang='ts'>
import { defineComponent, onMounted, ref } from 'vue'

export default defineComponent({
  name: '',
  setup() {
    const header = ref(null)
    const url = ref('')
    onMounted(() => {
      //回调函数
      const callback = (entries) => {
        const myEntry = entries[0]
        if (myEntry.isIntersecting) {
          console.log('~ 预加载图片开始')
          url.value =
            '//img10.360buyimg.com/imgzone/jfs/t1/197235/15/2956/67824/6115e076Ede17a418/d1350d4d5e52ef50.jpg'
        }
      }
      //配置对象
      const options = {
        root: null,
        rootMargin: '200px 200px 200px 200px',
        threshold: [0],
        trackVisibility: true,
        delay: 100,
      }
      //观察器实例
      const myObserver = new IntersectionObserver(callback, options)
      //开始观察
      myObserver.observe(header.value)
    })

    return { header, url }
  },
})
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.box {
}
.header {
  width: 100%;
  height: 400px;
  background-color: blue;
  color: aliceblue;
  font-size: 40px;
  text-align: center;
  line-height: 400px;
}
.header img {
  width: 100%;
  height: 100%;
}
.reference {
  width: 100%;
  visibility: hidden;
}
.vbody {
  width: 100%;
  height: 800px;
  background-color: red;
  color: aliceblue;
  font-size: 40px;
  text-align: center;
  line-height: 800px;
  margin: 10px 0;
}
</style>

利用 optionsrootMargin属性,可以在图片即将进入可视区域的时间进行图片的加载,即避免了提前请求大量图片造成的性能问题,也避免了图片进入窗口才加载已经来不及的问题。

吸顶

<template>
  <div class="box">
    <div class="reference"
         ref='reference'></div>
    <div class="header"
         ref='header'>吸顶区域</div>
    <div class="vbody">内容区域</div>
    <div class="vbody">内容区域</div>
    <div class="vbody">内容区域</div>
  </div>
</template>

<script lang='ts'>
import { defineComponent, onMounted, ref } from 'vue'

export default defineComponent({
  name: '',
  setup() {
    const header = ref(null)
    const reference = ref(null)

    onMounted(() => {
      //回调函数
      const callback = (entries) => {
        const myEntry = entries[0]
        if (!myEntry.isIntersecting) {
          console.log('~ 触发了吸顶')
          header.value.style.position = 'fixed'
          header.value.style.top = '0px'
        } else {
          console.log('~ 取消吸顶')
          header.value.style.position = 'relative'
        }
      }
      //配置对象
      const options = {
        root: null,
        rootMargin: '0px 0px 0px 0px',
        threshold: [0, 1],
        trackVisibility: true,
        delay: 100,
      }
      //观察器实例
      const myObserver = new IntersectionObserver(callback, options)
      //开始观察
      myObserver.observe(reference.value)
    })

    return { reference, header }
  },
})
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.header {
  width: 100%;
  height: 100px;
  background-color: blue;
  color: aliceblue;
  font-size: 40px;
  text-align: center;
  line-height: 100px;
}
.reference {
  width: 100%;
  visibility: hidden;
}
.vbody {
  width: 100%;
  height: 800px;
  background-color: red;
  color: aliceblue;
  font-size: 40px;
  text-align: center;
  line-height: 800px;
  margin: 10px 0;
}
</style>

思路就是利用一个参考元素作为交叉观察器观察的对象,当参考元素可见的时候,取消吸顶区域的 fixed 属性,否则添加 fixed 属性,吸底稍微复杂一点,但是道理差不多,留给小伙伴们自行研究吧 ~ ~。

埋点上报

<template>
  <div class="box">
    <div class="vbody">内容区域</div>
    <div class="vbody">内容区域</div>
    <div class="header"
         ref='header'>埋点区域</div>
    <div class="vbody">内容区域</div>

  </div>
</template>

<script lang='ts'>
import { defineComponent, onMounted, ref } from 'vue'

export default defineComponent({
  name: '',
  setup() {
    const header = ref(null)

    onMounted(() => {
      //回调函数
      const callback = (entries) => {
        const myEntry = entries[0]
        if (myEntry.isIntersecting) {
          console.log('~ 触发了埋点')
        }
      }
      //配置对象
      const options = {
        root: null,
        rootMargin: '0px 0px 0px 0px',
        threshold: [0.5],
        trackVisibility: true,
        delay: 100,
      }
      //观察器实例
      const myObserver = new IntersectionObserver(callback, options)
      //开始观察
      myObserver.observe(header.value)
    })

    return { header }
  },
})
</script>

<style>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
}
.header {
  width: 100%;
  height: 400px;
  background-color: blue;
  color: aliceblue;
  font-size: 40px;
  text-align: center;
  line-height: 400px;
}
.vbody {
  width: 100%;
  height: 800px;
  background-color: red;
  color: aliceblue;
  font-size: 40px;
  text-align: center;
  line-height: 800px;
  margin: 10px 0;
}
</style>

通常情况下,我们统计一个元素是否被用户有效的看到,并不是元素刚出现就触发埋点,而是元素进入可是区域一定比例才可以,我们可以配置 optionsthreshold0.5

等等等等。。。。

这个 api 可以说是非常强大了,可玩性也是极高,大家自由发挥 ~ ~

兼容性

为什么有两张兼容性的图呢?因为 trackVisibilitydelay 两个属性是属于 IntersectionObserver V2 的。所以小伙伴们在用的时候一定要注意兼容性。当然也有兼容解决方案,那就是 intersection-observer-polyfill

参考资料

[1] Can I Use:

https://caniuse.com/?search=IntersectionObserver%20

[2] MDN Intersection Observer:

https://developer.mozilla.org/zh-CN/docs/Web/API/IntersectionObserver

[3] IntersectionObserver API 使用教程:

https://www.ruanyifeng.com/blog/2016/11/intersectionobserver_api.html

[4] intersection-observer-polyfill:

https://www.npmjs.com/package/intersection-observer-polyfill

本文由哈喽比特于3年以前收录,如有侵权请联系我们。
文章来源:https://mp.weixin.qq.com/s/fu6VqPfWn7mB7evopnDeLA

 相关推荐

刘强东夫妇:“移民美国”传言被驳斥

京东创始人刘强东和其妻子章泽天最近成为了互联网舆论关注的焦点。有关他们“移民美国”和在美国购买豪宅的传言在互联网上广泛传播。然而,京东官方通过微博发言人发布的消息澄清了这些传言,称这些言论纯属虚假信息和蓄意捏造。

发布于:1年以前  |  808次阅读  |  详细内容 »

博主曝三大运营商,将集体采购百万台华为Mate60系列

日前,据博主“@超能数码君老周”爆料,国内三大运营商中国移动、中国电信和中国联通预计将集体采购百万台规模的华为Mate60系列手机。

发布于:1年以前  |  770次阅读  |  详细内容 »

ASML CEO警告:出口管制不是可行做法,不要“逼迫中国大陆创新”

据报道,荷兰半导体设备公司ASML正看到美国对华遏制政策的负面影响。阿斯麦(ASML)CEO彼得·温宁克在一档电视节目中分享了他对中国大陆问题以及该公司面临的出口管制和保护主义的看法。彼得曾在多个场合表达了他对出口管制以及中荷经济关系的担忧。

发布于:1年以前  |  756次阅读  |  详细内容 »

抖音中长视频App青桃更名抖音精选,字节再发力对抗B站

今年早些时候,抖音悄然上线了一款名为“青桃”的 App,Slogan 为“看见你的热爱”,根据应用介绍可知,“青桃”是一个属于年轻人的兴趣知识视频平台,由抖音官方出品的中长视频关联版本,整体风格有些类似B站。

发布于:1年以前  |  648次阅读  |  详细内容 »

威马CDO:中国每百户家庭仅17户有车

日前,威马汽车首席数据官梅松林转发了一份“世界各国地区拥车率排行榜”,同时,他发文表示:中国汽车普及率低于非洲国家尼日利亚,每百户家庭仅17户有车。意大利世界排名第一,每十户中九户有车。

发布于:1年以前  |  589次阅读  |  详细内容 »

研究发现维生素 C 等抗氧化剂会刺激癌症生长和转移

近日,一项新的研究发现,维生素 C 和 E 等抗氧化剂会激活一种机制,刺激癌症肿瘤中新血管的生长,帮助它们生长和扩散。

发布于:1年以前  |  449次阅读  |  详细内容 »

苹果据称正引入3D打印技术,用以生产智能手表的钢质底盘

据媒体援引消息人士报道,苹果公司正在测试使用3D打印技术来生产其智能手表的钢质底盘。消息传出后,3D系统一度大涨超10%,不过截至周三收盘,该股涨幅回落至2%以内。

发布于:1年以前  |  446次阅读  |  详细内容 »

千万级抖音网红秀才账号被封禁

9月2日,坐拥千万粉丝的网红主播“秀才”账号被封禁,在社交媒体平台上引发热议。平台相关负责人表示,“秀才”账号违反平台相关规定,已封禁。据知情人士透露,秀才近期被举报存在违法行为,这可能是他被封禁的部分原因。据悉,“秀才”年龄39岁,是安徽省亳州市蒙城县人,抖音网红,粉丝数量超1200万。他曾被称为“中老年...

发布于:1年以前  |  445次阅读  |  详细内容 »

亚马逊股东起诉公司和贝索斯,称其在购买卫星发射服务时忽视了 SpaceX

9月3日消息,亚马逊的一些股东,包括持有该公司股票的一家养老基金,日前对亚马逊、其创始人贝索斯和其董事会提起诉讼,指控他们在为 Project Kuiper 卫星星座项目购买发射服务时“违反了信义义务”。

发布于:1年以前  |  444次阅读  |  详细内容 »

苹果上线AppsbyApple网站,以推广自家应用程序

据消息,为推广自家应用,苹果现推出了一个名为“Apps by Apple”的网站,展示了苹果为旗下产品(如 iPhone、iPad、Apple Watch、Mac 和 Apple TV)开发的各种应用程序。

发布于:1年以前  |  442次阅读  |  详细内容 »

特斯拉美国降价引发投资者不满:“这是短期麻醉剂”

特斯拉本周在美国大幅下调Model S和X售价,引发了该公司一些最坚定支持者的不满。知名特斯拉多头、未来基金(Future Fund)管理合伙人加里·布莱克发帖称,降价是一种“短期麻醉剂”,会让潜在客户等待进一步降价。

发布于:1年以前  |  441次阅读  |  详细内容 »

光刻机巨头阿斯麦:拿到许可,继续对华出口

据外媒9月2日报道,荷兰半导体设备制造商阿斯麦称,尽管荷兰政府颁布的半导体设备出口管制新规9月正式生效,但该公司已获得在2023年底以前向中国运送受限制芯片制造机器的许可。

发布于:1年以前  |  437次阅读  |  详细内容 »

马斯克与库克首次隔空合作:为苹果提供卫星服务

近日,根据美国证券交易委员会的文件显示,苹果卫星服务提供商 Globalstar 近期向马斯克旗下的 SpaceX 支付 6400 万美元(约 4.65 亿元人民币)。用于在 2023-2025 年期间,发射卫星,进一步扩展苹果 iPhone 系列的 SOS 卫星服务。

发布于:1年以前  |  430次阅读  |  详细内容 »

𝕏(推特)调整隐私政策,可拿用户发布的信息训练 AI 模型

据报道,马斯克旗下社交平台𝕏(推特)日前调整了隐私政策,允许 𝕏 使用用户发布的信息来训练其人工智能(AI)模型。新的隐私政策将于 9 月 29 日生效。新政策规定,𝕏可能会使用所收集到的平台信息和公开可用的信息,来帮助训练 𝕏 的机器学习或人工智能模型。

发布于:1年以前  |  428次阅读  |  详细内容 »

荣耀CEO谈华为手机回归:替老同事们高兴,对行业也是好事

9月2日,荣耀CEO赵明在采访中谈及华为手机回归时表示,替老同事们高兴,觉得手机行业,由于华为的回归,让竞争充满了更多的可能性和更多的魅力,对行业来说也是件好事。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI操控无人机能力超越人类冠军

《自然》30日发表的一篇论文报道了一个名为Swift的人工智能(AI)系统,该系统驾驶无人机的能力可在真实世界中一对一冠军赛里战胜人类对手。

发布于:1年以前  |  423次阅读  |  详细内容 »

AI生成的蘑菇科普书存在可致命错误

近日,非营利组织纽约真菌学会(NYMS)发出警告,表示亚马逊为代表的电商平台上,充斥着各种AI生成的蘑菇觅食科普书籍,其中存在诸多错误。

发布于:1年以前  |  420次阅读  |  详细内容 »

社交媒体平台𝕏计划收集用户生物识别数据与工作教育经历

社交媒体平台𝕏(原推特)新隐私政策提到:“在您同意的情况下,我们可能出于安全、安保和身份识别目的收集和使用您的生物识别信息。”

发布于:1年以前  |  411次阅读  |  详细内容 »

国产扫地机器人热销欧洲,国产割草机器人抢占欧洲草坪

2023年德国柏林消费电子展上,各大企业都带来了最新的理念和产品,而高端化、本土化的中国产品正在不断吸引欧洲等国际市场的目光。

发布于:1年以前  |  406次阅读  |  详细内容 »

罗永浩吐槽iPhone15和14不会有区别,除了序列号变了

罗永浩日前在直播中吐槽苹果即将推出的 iPhone 新品,具体内容为:“以我对我‘子公司’的了解,我认为 iPhone 15 跟 iPhone 14 不会有什么区别的,除了序(列)号变了,这个‘不要脸’的东西,这个‘臭厨子’。

发布于:1年以前  |  398次阅读  |  详细内容 »
 相关文章
Android插件化方案 5年以前  |  237231次阅读
vscode超好用的代码书签插件Bookmarks 2年以前  |  8065次阅读
 目录