webAPI学习笔记5——移动端网页特效和本地存储

news2024/11/16 15:54:22

一、移动端网页特效

1. 触屏事件

1.1 触屏事件概述

移动端浏览器兼容性较好,我们不需要考虑以前 JS 的兼容性问题,可以放心的使用原生 JS 书写效果,但是移动端也有自己独特的地方。比如触屏事件 touch(也称触摸事件),Android 和 IOS 都有。
touch 对象代表一个触摸点。触摸点可能是一根手指,也可能是一根触摸笔。触屏事件可响应用户手指(或触控笔)对屏幕或者触控板操作。

(在电脑上的手机模式中,touchstart即需要点击鼠标)
常见的触屏事件如下:

1.2 触摸事件对象(TouchEvent)

TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等

touchstart、touchmove、touchend 三个事件都会各自有事件对象。

触摸事件对象重点我们看三个常见对象列表:

 因为平时我们都是给元素注册触摸事件,所以重点记住 targetTocuhes

3.3.20素材-07touch触摸事件

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div {
            width: 100px;
            height: 100px;
            background-color: pink;
        }
    </style>
</head>

<body>
    <div></div>
    <script>
        // 触摸事件对象
        // 1.获取元素
        // 2.手指触摸DOM元素事件
        var div = document.querySelector('div');
        div.addEventListener('touchstart', function (e) {
            // console.log(e);
            // touches—— 正在触摸 屏幕 的所有手指的一个 列表
            // targetTouches 正在触摸 当前DOM元素 的手指 列表
            // 如果侦听的是一个DOM元素,他们两个(即touches、targetTouches)是一样的
            // changedTouches 手指状态发生改变的列表 从无到有 或者 从有到无
            // 因为我们一般是触摸元素 所以经常使用的是 targetTouches
            console.log(e.targetTouches[0]);
            // targetTouches[0]就可以得到正在触摸dom元素的第一个手指的相关信息 比如 手指的坐标等等
        });
        // 3.手指在DOM元素身上移动事件
        div.addEventListener('touchmove', function () {
            
        });
        // 4.手指离开DOM元素事件
        div.addEventListener('touchend', function (e) {
            // console.log(e);
            // 当我们手指离开屏幕的时候,就没有了 touches 和 targetTouches,但是会有changedTouches
        });


    </script>
</body>

</html>

1.3 移动端拖动元素!!

  1. touchstart、touchmove、touchend 可以实现拖动元素
  2.  但是拖动元素需要当前手指的坐标值                                                                                      我们可以使用 targetTouches[0] 里面的pageX 和 pageY
  3. 移动端拖动的原理: 手指移动中,计算出手指移动的距离。然后用盒子原来的位置 + 手指移动的距离   (即盒子最终的坐标)
  4.  手指移动的距离: 手指滑动中的位置 减去 手指刚开始触摸的位置

拖动元素三步曲:

(1) 触摸元素 touchstart: 获取手指初始坐标,同时获得盒子原来的位置
(2) 移动手指 touchmove: 计算手指的滑动距离,并且移动盒子
(3) 离开手指 touchend:

注意: 手指移动也会触发滚动屏幕所以这里要阻止默认的屏幕滚动-- e.preventDefault();

<body>
    <div></div>
    <script>
        // (1) 触摸元素 touchstart:  获取手指初始坐标,同时获得盒子原来的位置
        // (2) 移动手指 touchmove:  计算手指的滑动距离,并且移动盒子
        // (3) 离开手指 touchend:
        var div = document.querySelector('div');
        var startX = 0;      // 获取手指的初始坐标(startX,startY)
        var startY = 0;
        var x = 0;           // 获得盒子原来的位置(x,y)
        var y = 0; 
        div.addEventListener('touchstart',function(e) {
            startX = e.targetTouches[0].pageX;
            startY= e.targetTouches[0].pageY;
            x = this.offsetLeft;
            y = this.offsetTop;
        });
        div.addEventListener('touchmove',function(e) {
            // 计算手指的移动距离:手指移动之后的坐标 减去 手指初始的坐标
            var moveX = e.targetTouches[0].pageX - startX;
            var moveY = e.targetTouches[0].pageY - startY;
            // 移动我们的盒子 盒子最终的坐标 = 盒子原来的坐标 + 手指移动的距离
            this.style.left = x + moveX + 'px';
            this.style.top = y + moveY + 'px';
            // 阻止默认的屏幕滚动-- e.preventDefault();
            e.preventDefault();
        });


        
    </script>
</body>

2. 移动端常见特效

2.1案例:移动端轮播图

移动端轮播图功能和基本PC端一致

  1. 可以自动播放图片
  2. 手指可以拖动播放轮播图

案例分析 

  • 小圆点跟随变化效果
  1. 把ol里面li带有current类名的选出来,去掉类名  remove
  2. 让当前索引号的小li加上current   add 
  3. 但是,是等着过渡结束之后变化,所以这个写到transitionend事件里面
  • 手指滑动轮播图 
  1. 本质就是ul跟随手指移动,简单说就是移动端拖动元素 
  2. 触摸元素 touchstart:获取手指初始坐标
  3. 移动手指 touchmove:计算手指的滑动距离,并且移动盒子
  4. 离开手指 touchend :根据滑动的距离分不同的情况
  5. 如果移动距离小于某个像素 就回弹原来位置
  6. 如果移动距离大于某个像素 就上一张下一张滑动

2.2 classList属性 

是HTML5新增的一个属性,返回元素的类名。但是IE10以上的版本支持

该属性用于在元素中添加,移除及切换CSS类。有以下方法:

  • 添加类add

element.classList.add(' 类名 ');

  • 删除类remove

 element.classList.remove(' 类名 ');

  • 切换类toggle

  element.classList.toggle(' 类名 ');

意思是,若原来有这个类名,就删掉;若无,就删掉

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        .bg {
            background-color: black;
        }
    </style>
</head>

<body>
    <div class="one two"></div>
    <button> 开关灯</button>
    <script>
        // classList 返回元素的类名
        var div = document.querySelector('div');
        // console.log(div.classList);        // DOMTokenList(2) ['one', 'two', value: 'one two']
        // console.log(div.classList[1]);       // two

        // 1.添加类名  是在后面追加类名不会覆盖以前的类名
        div.classList.add('three');     // class="one two three"
        // 2.删除类名
        div.classList.remove('one');     // class="two three"
        // 3.切换类名
        var btn = document.querySelector('button');
        btn.addEventListener('click',function() {
            document.body.classList.toggle('bg');
            // 若body有这个类名bg,则删除类bg;若无,则添加,即<body class = "bg">...</body>
        })
    </script>
</body>

</html>

2.3 click延时解决方案

移动端click事件会有300ms的延时,原因是移动端屏幕双击会缩放(double tap to zoom)页面。

解决方案:

  • 1. 禁用缩放。浏览器禁用默认的双击缩放行为并且去掉300ms的点击延迟。(但是有的页面需要缩放)

<meta name = "viewport" content = "user-scalable = no">  

  • 2. 利用touch事件自己封装这个事件解决300ms延迟(封装函数太麻烦)

原理就是:

  1. 当我们手指触摸屏幕,记录当前触摸时间
  2. 当我们手指离开屏幕,用离开的时间减去触摸的时间
  3. 如果时间小于150ms,并且没有滑过屏幕,那么我们就定义为点击
// 封装tap,解决click  300ms 延时
function tap (obj, callback) {
    var isMove = false;
    var startTime = 0;     // 记录触摸时候的时间变量
    obj.addEventListener('touchstart', function(e) {
        startTime = Date.now();      // 记录触摸时间
    });
    obj.addEventListener('touchmove', function(e) {
        isMove = true;      // 看是否有滑动,有滑动算拖拽,不算点击
    });
    obj.addEventListener('touchend', function(e) {
        if (!isMove && (Date.now() - startTime) < 150) {   // 如果手指触摸和离开时间小于150ms 算点击
            callback && callback();   // 执行回调函数
        }
        isMove = false;         // 取反 重置
        startTime = 0;
    });
})
// 调用
tap(div, function() {   // 执行代码     });
  •  3.运用 fastclick插件解决300ms延迟。GitHub官网地址:https://github.com/ftlabs/fastclick

 (fastclick.js在lib文件里面)

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
    <style>
        div {
            width: 50px;
            height: 50px;
            background-color: pink;
        }
    </style>
    <script src="fastclick.js"></script>
</head>

<body>
    <div></div>
    <script>
        // DOMContentLoaded表示“等着页面里面DOM元素加载完毕之后来执行”
        if ('addEventListener' in document) {
            document.addEventListener('DOMContentLoaded', function () {
                FastClick.attach(document.body);
            }, false);
        }
        // 上面这个if语句是引用fastclick.js里面的代码,来解决网页延迟

        var div = document.querySelector('div');
        div.addEventListener('click',function() {
            alert(11);
        })


    </script>
</body>

</html>

 

3. 移动端常用开发插件

3.1 什么是插件

移动端要求的是快速开发,所以我们经常会借助于一些插件来帮我完成操作,那么什么是插件呢?
JS 插件是 js 文件,它遵循一定规范编写,方便程序展示效果,拥有特定功能且方便调用。如轮播图和瀑布流插件。
特点:它一般是为了解决某个问题而专门存在,其功能单一,并且比较小。

我们以前写的animate.js也算一个最简单的插件。

fastclick插件解决300ms延迟。GitHub官网地址:https://github.com/ftlabs/fastclick

比如移动端常见插件:iScroll、Swiper、SuperSlider 。

3.2 插件的使用

1. 引入 js 插件文件。
2. 按照规定语法使用。Usage

3.3 Swiper 插件的使用(触摸滑动插件)

中文官网地址: https://www.swiper.com.cn/

(3.3.24素材04)

3.4 其他移动端常见插件

  • superslide: http://www.superslide2.com
  • iscroll : https://github.com/cubiq/iscroll

3.5 插件的使用总结

  1. 确认插件实现的功能
  2. 去官网查看使用说明
  3. 下载插件
  4. 打开demo实例文件,查看需要引入的相关文件,并且引入
  5. 复制demo实例文件中的结构html,样式css以及js代码

4. 移动端常用开发框架

4.1 框架概述

框架,顾名思义就是一套架构,它会基于自身的特点向用户提供一套较为完整的解决方案。框架的控制权在框架本身,使用者要按照框架所规定的某种规范进行开发。

前端常用的框架Bootstrap、Vue、Angular、React 等。即能开发PC端,也能开发移动端。

前端常用的移动端插件swiper、superslide、iscroll等。

4.2 Bootstrap

Bootstrap 是一个简洁、直观、强悍的前端开发框架,它让 web 开发更迅速、简单。

它能开放PC端,也能开发移动端

Bootstrap JS插件使用步骤:

  1. 引入相关JS文件
  2. 复制HTML结构
  3. 修改对应样式
  4. 修改相应JS参数

4.3 MUI 原生UI前端框架

MUI 是一个专门用于做手机 APP 的前端框架
MUI 的 UI 设计理念是:以 IOS 为基础,补充 Android 平台特有的控件。因此 MUI 封装的控件,UI 上更符合app 的体验。
MUI 中文官网地址:http://dev.dcloud.net.cn/mui/

二、本地存储

1. 本地存储

随着互联网的快速发展,基于网页的应用越来越普遍,同时也变的越来越复杂,为了满足各种各样的需求,会经常性在本地存储大量的数据,HTML5规范提出了相关解决方案。

本地存储特性

1、数据存储在用户浏览器
2、设置、读取方便、甚至页面刷新不丢失数据
3、容量较大,sessionStorage约5M、localStorage约20M
4、只能存储字符串,可以将对象JSON.stringify() 编码后存储

2. window.sessionStorage

1、生命周期为关闭浏览器窗口   (即,关闭浏览器,数据会消失,不会被存储)
2、在同一个窗口(页面)下数据可以共享
3. 以键值对的形式存储使用

存储数据:

sessionStorage.setItem(key, value);

获取数据:

sessionStorage.getItem(key);

删除数据

sessionStorage.removeItem(key);

删除所有数据

sessionStorage.clear();

<body>
    <input type="text">
    <button class="set">存储数据</button>
    <button class="get">获取数据</button>
    <button class="remove">删除数据</button>
    <button class="del">清空所有数据</button>
    <script>
        var ipt = document.querySelector('input');
        var set = document.querySelector('.set');
        var get = document.querySelector('.get');
        var remove = document.querySelector('.remove');
        var del = document.querySelector('.del');
        set.addEventListener('click',function() {
            // 当我们点击了之后,就可以把表单里面的值存储起来
            var val = ipt.value;
            sessionStorage.setItem('uname',val);
            sessionStorage.setItem('pwd',val);
        });
        get.addEventListener('click',function() {
            // 当我们点击了之后,就可以把表单里面的值获取
            console.log(sessionStorage.getItem('uname'));
        });
        remove.addEventListener('click',function() {
            // 当我们点击了之后,就可以把表单里面的值删除
            sessionStorage.removeItem('uname');
        });
        del.addEventListener('click',function() {
            // 当我们点击了之后,就可以把表单里面的值全部清空
            sessionStorage.clear();
        });




       
    </script>
</body>

 

3. window.localStorage

1、生命周期永久生效,除非手动删除 否则关闭页面也会存在
2、可以多窗口(页面)共享(同一浏览器可以共享)
3. 以键值对的形式存储使用

存储数据

localStorage.setItem(key, value);

获取数据

localStorage.getItem(key);   !! 如果console.log(localStorage.getItem(key)) 得到的是 

删除数据:

localStorage.removeItem(key);

删除所有数据:

localStorage.clear();

<body>
    <input type="text">
    <button class="set">存储数据</button>
    <button class="get">获取数据</button>
    <button class="remove">删除数据</button>
    <button class="del">清空所有数据</button>
    <script>
        var ipt = document.querySelector('input');
        var set = document.querySelector('.set');
        var get = document.querySelector('.get');
        var remove = document.querySelector('.remove');
        var del = document.querySelector('.del');
        set.addEventListener('click',function() {
            var val = ipt.value;
            localStorage.setItem('username',val);
        });
        get.addEventListener('click',function() {
            console.log(localStorage.getItem('username'));
        });
        remove.addEventListener('click',function() {
            localStorage.removeItem('username');
        });
        del.addEventListener('click',function() {
            localStorage.clear();
        });




    </script>
</body>

案例:记住用户名

如果勾选记住用户名, 下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名

<body>
    <input type="text" id="username"> <input type="checkbox" name="" id="remember"> 记住用户名
    <script>
        // 案例:记住用户名
        // 如果勾选记住用户名, 下次用户打开浏览器,就在文本框里面自动显示上次登录的用户名
        // ① 把数据存起来,用到本地存储
        // ② 关闭页面,也可以显示用户名,所以用到localStorage
        // ③ 打开页面,先判断是否有这个用户名,如果有,就在表单里面显示用户名,并且勾选复选框
        // ④ 当复选框发生改变的时候 change事件
        // ⑤ 如果勾选,就存储,否则就移除
        var username = document.querySelector('#username');
        var remember = document.querySelector('#remember');
        if (localStorage.getItem('username')) {      // 表示 如果值存在,则执行if语句
            username.value = localStorage.getItem('username');
            remember.checked = true;  // body里面"checkbox"表示复选框  这里true表示复选框里面打钩钩
        }
        remember.addEventListener('change',function() {
            if (this.checked) {      // 表示复选框是否打钩 这里是“是”
                localStorage.setItem('username',username.value);
            } else {
                localStorage.removeItem('username');
            }
        })


        
    </script>
</body>

 

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

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

相关文章

联想M7605DW怎么连接WiFi网络

联想M7605DW是一款拥有WiFi功能的打印机&#xff0c;可以通过WiFi连接无线网络&#xff0c;实现打印无线传输。 首先&#xff0c;需要确保你的WiFi网络已经正常连接&#xff0c;并且知道WiFI的网络名称和密码&#xff0c;同时确保你的电脑或手机设备与WiFi相连接。 启动联想M76…

数组、指针练习题及解析(含笔试题目讲解)其一

目录 前言 题目列表&#xff1a; 题目解析 一维数组 字符数组 字符串 字符指针 二维数组 笔试题 总结 前言 前几期的博客已经将有关指针、数组的所以知识都已基本讲解完毕&#xff0c;那么接下来我们就做一些练习巩固&#xff0c;这些练习依据历年来一些公司笔试题进行…

java的ThreadLocal变量

Java的ThreadLocal变量是线程的局部变量&#xff0c;只能被本线程访问&#xff0c;不能被其它线程访问&#xff0c;也就是说线程间进行了隔离。每个线程访问该变量的一个独立拷贝&#xff0c;互相不干扰。感觉跟synchronized的作用相反&#xff0c;synchronized是为了保护线程间…

Kafka入门,mysql5.7 Kafka-Eagle部署(二十五)

官网 https://www.kafka-eagle.org/ 下载解压 这里使用的是2.0.8 创建mysql数据库 创建名为ke数据库,新版本会自动创建&#xff0c;不会创建的话&#xff0c;自己手动创建&#xff0c;不然会报查不到相关表信息错误 SET NAMES utf8; SET FOREIGN_KEY_CHECKS 0;-- ------…

从2023中国峰会,看亚马逊云科技的生成式AI战略

“生成式AI的发展就像一场马拉松比赛&#xff0c;当比赛刚刚开始时&#xff0c;如果只跑了三四步就断言某某会赢得这场比赛&#xff0c;显然是不合理的。我们现在还处于非常早期的阶段。” 近日&#xff0c;在2023亚马逊云科技中国峰会上&#xff0c;亚马逊云科技全球产品副总裁…

智慧农业:温室大棚物联网系统,助力实现可视化科学管理

我国传统农业的特点是靠天吃饭&#xff0c;而智慧农业发端于物联网设备和对应的农业信息化管理系统&#xff0c;是利用数字技术、数据分析和人工智能等先进技术手段&#xff0c;对农业生产进行精细化管理和智能化决策的一种新型农业生产模式。它可以通过实时监测、预测和调控土…

java 配置打包Spring Boot项目过程中跳过测试环节

上文 java 打包Spring Boot项目&#xff0c;并运行在windows系统中中 我们演示了打包 Spring Boot项目的并运行在本地的方法 但是 我们这里会看到 每次打包 他这都会有个T E S T S 测试的部分 但是 我们自己开发的程序 要上线 有没有问题我们肯定自己清楚啊 没必要它做测试 而且…

web学习笔记2

文档流 网页是一个多层的结构&#xff0c;设置样式也是一层一层的设置&#xff0c;最终我们看到的最上面的一层。 文档流是网页最底层 我们创建的元素默认情况下&#xff0c;都在文档流中 元素分为两种状态&#xff1a;在文档流中&#xff0c;脱离文档流 元素在文档流中的特点 …

同一段数据分别做傅里叶变化和逆变换的结果及分析

已知有公式 D F T &#xff1a; X [ k ] ∑ n 0 N − 1 x [ n ] e − j 2 π k n N &#xff0c; 0 ≤ k ≤ N − 1 DFT&#xff1a;Χ[k]\sum_{n0}^{N-1}x[n]e^{-\frac{j2\pi kn}{N}}&#xff0c;0≤k≤N-1 DFT&#xff1a;X[k]n0∑N−1​x[n]e−Nj2πkn​&#xff0c;0≤k…

超详细 | 模拟退火-粒子群自适应优化算法及其实现(Matlab)

作者在前面的文章中介绍了经典的优化算法——粒子群算法(PSO)&#xff0c;各种智能优化算法解决问题的方式和角度各不相同&#xff0c;都有各自的适用域和局限性&#xff0c;对智能优化算法自身做的改进在算法性能方面得到了一定程度的提升&#xff0c;但算法缺点的解决并不彻底…

学生公寓智能电表控电系统的技术要求

学生公寓电表智能控电石家庄光大远通电气有限公司模块采用高精度计量芯片,的计量计费功能。 控制路数&#xff1a;可输出1~4路输出,每个回路都可以设置负载识别,定时断送过载功率等控电参数。 自动断电 &#xff1a;具有自动断电功能,可用电量为0时,应自动切断该分路电源 支持正…

创建Spring CloudDEMO流程

创建普通的maven工程作为父工程 然后设置字符集为UTF-8 再注解生效激活 java编译版本选择8 idea文件忽略&#xff08;忽略乱七八糟的文件&#xff09; *.hprof;*.pyc;*.pyo;*.rbc;*.yarb;*~;.DS_Store;.git;.hg;.svn;CVS;__pycache__;_svn;vssver.scc;vssver2.scc;.idea;*.iml…

TencentOS3.1安装PHP+Nginx+redis测试系统

PHP和Nginx应用统一安装在/application下。 Nginx选用了较新的版本1.25.0 官网下载安装包&#xff0c;解包。执行如下命令编译&#xff1a; ./configure --prefix/application/nginx-1.25.0 --usernginx --groupnginx --with-http_ssl_module --with-http_stub_status_modu…

win系统电脑在线打开sketch文件的方法

自Sketch诞生以来&#xff0c;只有Mac版本。Windows计算机如何在线打开Sketch文件&#xff1f; 即时设计已经解决了你遇到的大部分问题&#xff0c;不占用内存也是免费的。 您可以使用此软件直接在线打开Sketch文件&#xff0c;完整预览并导出CSS、SVG、PNG等&#xff0c;还具…

解析JSON格式数据

解析JSON格式数据 比起XML&#xff0c;JSON的体积更小&#xff0c;语义性更差 传入的JSON文件如下 使用JSONObject private fun parseJSONWithJSONObject(jsonData: String) { try { val jsonArray JSONArray(jsonData) for (i in 0 until jsonArray.length()){ val j…

视频去除水印怎么弄?这几个实用方法分享给大家!

在我们观看或分享视频时&#xff0c;可能会遇到一些带有水印的视频。这些水印可能会影响我们的观看体验&#xff0c;或者在我们需要使用这些视频时造成不便。下面&#xff0c;我将为你介绍三种去除视频水印的方法。 方法一&#xff1a;使用记灵在线工具 记灵在线工具是一个非…

Leetcode:684. 冗余连接(并查集C++)

目录 684. 冗余连接 题目描述&#xff1a; 实现代码与解析&#xff1a; 并查集 原理思路&#xff1a; 684. 冗余连接 题目描述&#xff1a; 树可以看成是一个连通且 无环 的 无向 图。 给定往一棵 n 个节点 (节点值 1&#xff5e;n) 的树中添加一条边后的图。添加的边的…

Python安装解释器

文章目录 一、下载Python解释器二. Linux环境的安装三. pycharm创建项目四、验证安装是否成功 一、下载Python解释器 首先&#xff0c;您需要从官方Python网站&#xff08;https://python.org&#xff09;下载Python解释器。Python的当前稳定版本是3.9.x系列。网站上提供了针对…

Anoym:一种以组合质押策略为特点的 LSD 设施

LSD&#xff08;Liquid Staking Derivatives&#xff09;&#xff0c;即流动性质押衍生品&#xff0c;目前主要包括Lido、Frax等主要项目&#xff0c;它是伴随着ETH 2.0升级成长起来的DeFi衍生品赛道。ETH 2.0 以 POS 为共识机制&#xff0c;节点需要质押 32 ETH 才能参与网络维…

三分钟了解 RocketMQ消息队列

文章目录 基本概念详细介绍主题&#xff08;Topic&#xff09;消息类型&#xff08;MessageType&#xff09;消息队列&#xff08;MessageQueue&#xff09;消息&#xff08;Message&#xff09;消息视图&#xff08;MessageView&#xff09;消息标签&#xff08;MessageTag&am…