HBuilderX

HBuilderX

极客开发工具
uni-app

uni-app

开发一次,多端覆盖
uniCloud

uniCloud

云开发平台
HTML5+

HTML5+

增强HTML5的功能体验
MUI

MUI

上万Star的前端框架

uni-app 中退出应用

uniapp

在 App 环境下,可以直接调用 5+ API 来实现。

// #ifdef APP-PLUS  
plus.runtime.quit();  
// #endif

注意:iOS 平台不支持此 API,只能是用户主动杀进程。

微信小程序平台下,2.1.0 版本开始,navigate 支持设置 open-type 为 exit 来退出。

<navigator target="miniProgram" open-type="exit">退出小程序</navigator>
继续阅读 »

在 App 环境下,可以直接调用 5+ API 来实现。

// #ifdef APP-PLUS  
plus.runtime.quit();  
// #endif

注意:iOS 平台不支持此 API,只能是用户主动杀进程。

微信小程序平台下,2.1.0 版本开始,navigate 支持设置 open-type 为 exit 来退出。

<navigator target="miniProgram" open-type="exit">退出小程序</navigator>
收起阅读 »

Android开发中XRecyclerview用法及遇到的一些问题

  目前通过xrecyclerview的开源代码来实现系列功能,加载数据传入type,值为1,2,3,分别表示初次加载,下拉刷新数据,上拉加载更多数据操作,刷新数据只需要重新放入数据,然后notifyDataSetChanged();即可.加载更多数据只需要在上拉时将获取数据放入之前数据list中刷新数据即可.这么说可能会比较抽象,下面通过代码来具体给大家演示一下是如何实现的吧。

  核心代码:

  首先需要配置xRecyclerview的属性:

  xRecyclerView.setPullRefreshEnabled(true);

  xRecyclerView.setLoadingMoreEnabled(true);

  xRecyclerView.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader);

  xRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.Pacman);

  xRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {

  @Override

  public void onRefresh() {

  new Handler().postDelayed(new Runnable(){

  public void run() {

  getWorksData(1,2);

  }

  }, 2000);

  }

  @Override

  public void onLoadMore() {

  count+=1;

  loge("第几次加载=="+count);

  new Handler().postDelayed(new Runnable(){

  public void run() {

  getWorksData(count,3);

  }

  }, 2000);

  }

  });

  从上面代码可以看到,需要先设置下拉刷新和加载更多可执行,为true,然后设置它的加载样式,有多种样式可以选择,具体参考xrecyclerview的github介绍,接下里设置一下loadingListener即可.我们需要实现它的两个方法:刷新回调方法onRefresh()和加载更多方法onLoadingMore(),即分别在这两个方法中实现自己的刷新和加载数据逻辑即可.

  getWorksData(参数1,参数2)即为获取后天数据的方法.参数1是指数据请求的次数,也指代请求后台的数据页数,初始化为1;参数2表示当前数据的操作模式(为1:初次请求数据,为2:刷新数据,为3:加载更多数据).

  loadingDialog.dismiss();

  creativeImageBean = JSONObject.parseObject(response, CreativeImageBean.class);

  if (creativeImageBean!=null && creativeImageBean.getCode() == 1000) {

  if(type==1){

  //是初次加载

  loge("初次加载数据");

  data=creativeImageBean.getData().getList();

  Message msg = Message.obtain(handler);

  msg.what = GET_DATA;

  handler.handleMessage(msg);

  }else if(type==2){

  //下拉刷新

  //data = new ArrayList();

  updateData=creativeImageBean.getData().getList();

  count = 1;

  loge("刷新了");

  xRecyclerView.refreshComplete();

  if (workAdapter != null) {

  Message msg = Message.obtain(handler);

  msg.what = UPDATE_DATA;

  handler.sendMessage(msg);

  } else {

  Message msg = Message.obtain(handler);

  msg.what = GET_DATA;

  handler.sendMessage(msg);

  }

  }else if(type==3){

  //加载更多

  //moreData = new ArrayList();

  moreData = creativeImageBean.getData().getList();

  loge("加载更多数据页数==" + count+"---数据量--"+moreData.size());

  if (moreData != null && moreData.size() > 0) {

  data.addAll(moreData);

  loge("data加载更多数据后的地址=="+data.hashCode());

  loge("作品总数据长度==" + data.size());

  // Message msg = Message.obtain(handler);

  // msg.what = UPDATE_DATA;

  // handler.sendMessage(msg);

  if(workAdapter!=null){

  loge("workAdapter不为空");

  workAdapter.notifyDataSetChanged();

  }else {

  loge("workAdapter为空");

  workAdapter = new WorksShowAdapter(MemoryWorksActivity.this,data);

  xRecyclerView.setAdapter(workAdapter);

  }

  xRecyclerView.loadMoreComplete();

  //workAdapter.addNewData(data);//添加数据

  //如果添加moreData,为空是什么原因

  } else {

  loge("moreData数据为空");

  // xRecyclerView.setLoadingMoreEnabled(false);

  // toast(getString(R.string.act_home_loadmore));

  xRecyclerView.loadMoreComplete();

  }

  }

  }

  刷新数据需要先清除原有数据,然后放入最新数据,接着调用adaper.motifyDataSetChange()方法.

  问题总览

  点击第i个item,提示却是第i+1个item被点击

  问题原因:xrecyclerrview默认添加的header和footer,这就导致item的真实位置发生了变化,

  解决方案:将item的位置由holder.getLayoutPosition()改为position就可以了,adapter中修改代码如下:

  holder.itemView.setOnClickListener(new View.OnClickListener() {

  @Override

  public void onClick(View v) {

  //int pos = holder.getLayoutPosition();//点击的item控件的位置

  myListener.onItemClick(holder.itemView, position);

  /*

  此方法返回的pos值与onBindViewHolder方法传入的position值有可能不同。

  根据SDK中的解释,在Recyclerview 进行添加、移除item等操作时,position位置可能会变化,

  而所有的adapter的刷新并不总是及时的,

  只有这个方法返回的才是当前item经过一些变换后所处的真正位置。

  */

  }

  });

  notifySetDataCahnged()无法刷新数据的问题:

  问题原因:先下拉刷新,然后上拉加载数据为空不显示,因为data的地址发生了变化,数据的刷新是在adapter中实现的,并且是新建一个list,然后调用了list.addAll(data),导致刷新data数据地址改变,刷新无效

  解决方案:新建一个list对象moreData来专门接收下拉刷新的数据,并且摒弃adapter中的addNewData()方法,直接data.clear()—data.addAll(moreData)—adapter.notifyDataChanged()

  虽然内容有些多,但是只要大家按照刚才分享的源码来一步一步的去做,都是可以做到的,如果还是存在不理解的地方,可以咨询我们哦。

  本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明原文作者及出处。

继续阅读 »

  目前通过xrecyclerview的开源代码来实现系列功能,加载数据传入type,值为1,2,3,分别表示初次加载,下拉刷新数据,上拉加载更多数据操作,刷新数据只需要重新放入数据,然后notifyDataSetChanged();即可.加载更多数据只需要在上拉时将获取数据放入之前数据list中刷新数据即可.这么说可能会比较抽象,下面通过代码来具体给大家演示一下是如何实现的吧。

  核心代码:

  首先需要配置xRecyclerview的属性:

  xRecyclerView.setPullRefreshEnabled(true);

  xRecyclerView.setLoadingMoreEnabled(true);

  xRecyclerView.setRefreshProgressStyle(ProgressStyle.BallSpinFadeLoader);

  xRecyclerView.setLoadingMoreProgressStyle(ProgressStyle.Pacman);

  xRecyclerView.setLoadingListener(new XRecyclerView.LoadingListener() {

  @Override

  public void onRefresh() {

  new Handler().postDelayed(new Runnable(){

  public void run() {

  getWorksData(1,2);

  }

  }, 2000);

  }

  @Override

  public void onLoadMore() {

  count+=1;

  loge("第几次加载=="+count);

  new Handler().postDelayed(new Runnable(){

  public void run() {

  getWorksData(count,3);

  }

  }, 2000);

  }

  });

  从上面代码可以看到,需要先设置下拉刷新和加载更多可执行,为true,然后设置它的加载样式,有多种样式可以选择,具体参考xrecyclerview的github介绍,接下里设置一下loadingListener即可.我们需要实现它的两个方法:刷新回调方法onRefresh()和加载更多方法onLoadingMore(),即分别在这两个方法中实现自己的刷新和加载数据逻辑即可.

  getWorksData(参数1,参数2)即为获取后天数据的方法.参数1是指数据请求的次数,也指代请求后台的数据页数,初始化为1;参数2表示当前数据的操作模式(为1:初次请求数据,为2:刷新数据,为3:加载更多数据).

  loadingDialog.dismiss();

  creativeImageBean = JSONObject.parseObject(response, CreativeImageBean.class);

  if (creativeImageBean!=null && creativeImageBean.getCode() == 1000) {

  if(type==1){

  //是初次加载

  loge("初次加载数据");

  data=creativeImageBean.getData().getList();

  Message msg = Message.obtain(handler);

  msg.what = GET_DATA;

  handler.handleMessage(msg);

  }else if(type==2){

  //下拉刷新

  //data = new ArrayList();

  updateData=creativeImageBean.getData().getList();

  count = 1;

  loge("刷新了");

  xRecyclerView.refreshComplete();

  if (workAdapter != null) {

  Message msg = Message.obtain(handler);

  msg.what = UPDATE_DATA;

  handler.sendMessage(msg);

  } else {

  Message msg = Message.obtain(handler);

  msg.what = GET_DATA;

  handler.sendMessage(msg);

  }

  }else if(type==3){

  //加载更多

  //moreData = new ArrayList();

  moreData = creativeImageBean.getData().getList();

  loge("加载更多数据页数==" + count+"---数据量--"+moreData.size());

  if (moreData != null && moreData.size() > 0) {

  data.addAll(moreData);

  loge("data加载更多数据后的地址=="+data.hashCode());

  loge("作品总数据长度==" + data.size());

  // Message msg = Message.obtain(handler);

  // msg.what = UPDATE_DATA;

  // handler.sendMessage(msg);

  if(workAdapter!=null){

  loge("workAdapter不为空");

  workAdapter.notifyDataSetChanged();

  }else {

  loge("workAdapter为空");

  workAdapter = new WorksShowAdapter(MemoryWorksActivity.this,data);

  xRecyclerView.setAdapter(workAdapter);

  }

  xRecyclerView.loadMoreComplete();

  //workAdapter.addNewData(data);//添加数据

  //如果添加moreData,为空是什么原因

  } else {

  loge("moreData数据为空");

  // xRecyclerView.setLoadingMoreEnabled(false);

  // toast(getString(R.string.act_home_loadmore));

  xRecyclerView.loadMoreComplete();

  }

  }

  }

  刷新数据需要先清除原有数据,然后放入最新数据,接着调用adaper.motifyDataSetChange()方法.

  问题总览

  点击第i个item,提示却是第i+1个item被点击

  问题原因:xrecyclerrview默认添加的header和footer,这就导致item的真实位置发生了变化,

  解决方案:将item的位置由holder.getLayoutPosition()改为position就可以了,adapter中修改代码如下:

  holder.itemView.setOnClickListener(new View.OnClickListener() {

  @Override

  public void onClick(View v) {

  //int pos = holder.getLayoutPosition();//点击的item控件的位置

  myListener.onItemClick(holder.itemView, position);

  /*

  此方法返回的pos值与onBindViewHolder方法传入的position值有可能不同。

  根据SDK中的解释,在Recyclerview 进行添加、移除item等操作时,position位置可能会变化,

  而所有的adapter的刷新并不总是及时的,

  只有这个方法返回的才是当前item经过一些变换后所处的真正位置。

  */

  }

  });

  notifySetDataCahnged()无法刷新数据的问题:

  问题原因:先下拉刷新,然后上拉加载数据为空不显示,因为data的地址发生了变化,数据的刷新是在adapter中实现的,并且是新建一个list,然后调用了list.addAll(data),导致刷新data数据地址改变,刷新无效

  解决方案:新建一个list对象moreData来专门接收下拉刷新的数据,并且摒弃adapter中的addNewData()方法,直接data.clear()—data.addAll(moreData)—adapter.notifyDataChanged()

  虽然内容有些多,但是只要大家按照刚才分享的源码来一步一步的去做,都是可以做到的,如果还是存在不理解的地方,可以咨询我们哦。

  本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明原文作者及出处。

收起阅读 »

拍照后获取图片的宽,高,大小等信息

h5+ 拍照
plus.camera.getCamera().captureImage(function(absPath) {  
        //创建一个 bitmap 对象,参数1是id,参数2是拍照返回的图片绝对路径  
    var bitmap = new plus.nativeObj.Bitmap('test', absPath);  
        //没办法,费点事,再用 bitmap 保存一下,overwrite 属性表示是否覆盖原来文件,第1个回调是保存成功,第2个回调是保存失败  
    bitmap.save(  
        absPath,   
                {  
            overwrite: true,  
            format: 'jpg',  

        },  
        function(event) {  
            // Code here  
            // 保存后的图片url路径,以"file://"开头  
            var target = event.target;  
            // 保存后图片的大小,单位为字节(Byte)  
            var size = event.size;  
            // 保存后图片的实际宽度,单位为px  
            var width = event.width;  
            // 保存后图片的实际高度,单位为px    
            var height = event.height;  
            console.log('图片绝对路径: ' + target)  
            console.log('图片文件大小:' + size)  
            console.log('图片宽度:' + width)  
            console.log('图片高度:' + height)  

            function bytesToSize(bytes) {  
                if (bytes === 0) {  
                    return '0 B'  
                }  
                var k = 1000, // or 1024  
                sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],  
                i = Math.floor(Math.log(bytes) / Math.log(k));  

                return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];  
            }  
            var ss = bytesToSize(size);  
            console.log(ss)  
        },  
        function(error) {  
                                            // Error.  
            var code = error.code; // 错误编码  
            var message = error.message; // 错误描述信息                
            console.log(code)  
            console.log(message)  
        }  
    );  
}

其实我觉得 new plus.nativeObj.Bitmap('test', absPath) 后就应该包含了该有的信息了....

继续阅读 »
plus.camera.getCamera().captureImage(function(absPath) {  
        //创建一个 bitmap 对象,参数1是id,参数2是拍照返回的图片绝对路径  
    var bitmap = new plus.nativeObj.Bitmap('test', absPath);  
        //没办法,费点事,再用 bitmap 保存一下,overwrite 属性表示是否覆盖原来文件,第1个回调是保存成功,第2个回调是保存失败  
    bitmap.save(  
        absPath,   
                {  
            overwrite: true,  
            format: 'jpg',  

        },  
        function(event) {  
            // Code here  
            // 保存后的图片url路径,以"file://"开头  
            var target = event.target;  
            // 保存后图片的大小,单位为字节(Byte)  
            var size = event.size;  
            // 保存后图片的实际宽度,单位为px  
            var width = event.width;  
            // 保存后图片的实际高度,单位为px    
            var height = event.height;  
            console.log('图片绝对路径: ' + target)  
            console.log('图片文件大小:' + size)  
            console.log('图片宽度:' + width)  
            console.log('图片高度:' + height)  

            function bytesToSize(bytes) {  
                if (bytes === 0) {  
                    return '0 B'  
                }  
                var k = 1000, // or 1024  
                sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'],  
                i = Math.floor(Math.log(bytes) / Math.log(k));  

                return (bytes / Math.pow(k, i)).toPrecision(3) + ' ' + sizes[i];  
            }  
            var ss = bytesToSize(size);  
            console.log(ss)  
        },  
        function(error) {  
                                            // Error.  
            var code = error.code; // 错误编码  
            var message = error.message; // 错误描述信息                
            console.log(code)  
            console.log(message)  
        }  
    );  
}

其实我觉得 new plus.nativeObj.Bitmap('test', absPath) 后就应该包含了该有的信息了....

收起阅读 »

HTML中JS对于字符串的切割截取实现

  在网站编程制作中,对于字符串的切割截取平时所用可能不是特别多,而且分的比较细,所以自备自查。有备无患。由于之前所有均在一个demo测试,下面就来跟大家分享一下成果。

  1.函数:split()

  功能:使用一个指定的分隔符把一个字符串分割存储到数组

  例子: str=”jpg|bmp|gif|ico|png”; arr=str.split(”|”);

  //arr是一个包含字符值”jpg”、”bmp”、”gif”、”ico”和”png”的数组

  2.函数:join()

  功能:使用您选择的分隔符将一个数组合并为一个字符串

  例子: var delimitedString=myArray.join(delimiter);

  var myList=new Array(”jpg”,”bmp”,”gif”,”ico”,”png”);

  var portableList=myList.join(”|”);

  //结果是jpg|bmp|gif|ico|png

  3.函数:concat()

  功能:将两个数组连接在一起;

  例子:arr1=[1,2,3,4]

  arr2=[5,6,7,8]

  alert(arr1.concat(arr2)) //结果为[1,2,3,4,5,6,7,8]

  4.函数:charAt()

  功能:返回指定位置的字符。字符串中第一个字符的下标是 0。如果参数 index 不在 0 与 string.length 之间,该方法将返回一个空字符串。

  例子:var str='a,g,i,d,o,v,w,d,k,p'

  alert(str.charAt(2)) //结果为g

  5:函数:charCodeAt()

  功能:charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。

  方法 charCodeAt() 与 charAt() 方法执行的操作相似,只不过前者返回的是位于指定位置的字符的编码,而后者返回的是字符子串。

  例子:var str='a,g,i,d,o,v,w,d,k,p'

  alert(str.charCodeAt(2)) //结果为103。即g的Unicode编码为103

  6.函数:slice()

  功能:arrayObject.slice(start,end)

  start:必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。

  end:可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。

  返回一个新的数组,包含从start到end(不包括该元素)的arrayobject中的元素。

  例子:var str='ahji3o3s4e6p8a0sdewqdasj'

  alert(str.slice(2,5)) //结果ji3

  7.函数:substring()

  定义和用法 substring 方法用于提取字符串中介于两个指定下标之间的字符。

  语法 stringObject.substring(start,stop)

  start 必需。一个非负的整数,规定要提取的子串的第一个字符在 stringObject 中的位置。

  stop 可选。一个非负的整数,比要提取的子串的最后一个字符在 stringObject 中的位置多 1。

  如果省略该参数,那么返回的子串会一直到字符串的结尾。

  返回 一个新的字符串,该字符串值包含 stringObject 的一个子字符串,其内容是从 start 处到 stop-1 处的所有字符,其长度为 stop 减 start。 说明 substring 方法返回的子串包括 start 处的字符,但不包括 end 处的字符。 如果 start 与 end 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。 如果 start 比 end 大,那么该方法在提取子串之前会先交换这两个参数。 如果 start 或 end 为负数,那么它将被替换为 0。

  例子:var str='ahji3o3s4e6p8a0sdewqdasj'

  alert(str.substring(2,6)) //结果为ji3o3

  8.函数:substr

  定义和用法 substr 方法用于返回一个从指定位置开始的指定长度的子字符串。

  语法 stringObject.substr(start [, length ])

  参数 start 必需。所需的子字符串的起始位置。字符串中的第一个字符的索引为 0。

  length 可选。在返回的子字符串中应包括的字符个数。 说明 如果 length 为 0 或负数,将返回一个空字符串。 如果没有指定该参数,则子字符串将延续到stringObject的最后。

  举例: var str = "0123456789";

  alert(str.substring(0));------------"0123456789"

  alert(str.substring(5));------------"56789"

  alert(str.substring(10));-----------""

  alert(str.substring(12));-----------""

  alert(str.substring(-5));-----------"0123456789"

  alert(str.substring(-10));----------"0123456789"

  alert(str.substring(-12));----------"0123456789"

  alert(str.substring(0,5));----------"01234"

  alert(str.substring(0,10));---------"0123456789"

  alert(str.substring(0,12));---------"0123456789"

  alert(str.substring(2,0));----------"01"

  alert(str.substring(2,2));----------""

  alert(str.substring(2,5));----------"234"

  alert(str.substring(2,12));---------"23456789"

  alert(str.substring(2,-2));---------"01"

  alert(str.substring(-1,5));---------"01234"

  alert(str.substring(-1,-5));--------""

  好了,到这里就跟大家分享完成了,如果大家还是不知道是怎么实现的,或者还存在有不理解的地方,都是可以留言咨询。

  本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!

继续阅读 »

  在网站编程制作中,对于字符串的切割截取平时所用可能不是特别多,而且分的比较细,所以自备自查。有备无患。由于之前所有均在一个demo测试,下面就来跟大家分享一下成果。

  1.函数:split()

  功能:使用一个指定的分隔符把一个字符串分割存储到数组

  例子: str=”jpg|bmp|gif|ico|png”; arr=str.split(”|”);

  //arr是一个包含字符值”jpg”、”bmp”、”gif”、”ico”和”png”的数组

  2.函数:join()

  功能:使用您选择的分隔符将一个数组合并为一个字符串

  例子: var delimitedString=myArray.join(delimiter);

  var myList=new Array(”jpg”,”bmp”,”gif”,”ico”,”png”);

  var portableList=myList.join(”|”);

  //结果是jpg|bmp|gif|ico|png

  3.函数:concat()

  功能:将两个数组连接在一起;

  例子:arr1=[1,2,3,4]

  arr2=[5,6,7,8]

  alert(arr1.concat(arr2)) //结果为[1,2,3,4,5,6,7,8]

  4.函数:charAt()

  功能:返回指定位置的字符。字符串中第一个字符的下标是 0。如果参数 index 不在 0 与 string.length 之间,该方法将返回一个空字符串。

  例子:var str='a,g,i,d,o,v,w,d,k,p'

  alert(str.charAt(2)) //结果为g

  5:函数:charCodeAt()

  功能:charCodeAt() 方法可返回指定位置的字符的 Unicode 编码。这个返回值是 0 - 65535 之间的整数。

  方法 charCodeAt() 与 charAt() 方法执行的操作相似,只不过前者返回的是位于指定位置的字符的编码,而后者返回的是字符子串。

  例子:var str='a,g,i,d,o,v,w,d,k,p'

  alert(str.charCodeAt(2)) //结果为103。即g的Unicode编码为103

  6.函数:slice()

  功能:arrayObject.slice(start,end)

  start:必需。规定从何处开始选取。如果是负数,那么它规定从数组尾部开始算起的位置。也就是说,-1 指最后一个元素,-2 指倒数第二个元素,以此类推。

  end:可选。规定从何处结束选取。该参数是数组片断结束处的数组下标。如果没有指定该参数,那么切分的数组包含从 start 到数组结束的所有元素。如果这个参数是负数,那么它规定的是从数组尾部开始算起的元素。

  返回一个新的数组,包含从start到end(不包括该元素)的arrayobject中的元素。

  例子:var str='ahji3o3s4e6p8a0sdewqdasj'

  alert(str.slice(2,5)) //结果ji3

  7.函数:substring()

  定义和用法 substring 方法用于提取字符串中介于两个指定下标之间的字符。

  语法 stringObject.substring(start,stop)

  start 必需。一个非负的整数,规定要提取的子串的第一个字符在 stringObject 中的位置。

  stop 可选。一个非负的整数,比要提取的子串的最后一个字符在 stringObject 中的位置多 1。

  如果省略该参数,那么返回的子串会一直到字符串的结尾。

  返回 一个新的字符串,该字符串值包含 stringObject 的一个子字符串,其内容是从 start 处到 stop-1 处的所有字符,其长度为 stop 减 start。 说明 substring 方法返回的子串包括 start 处的字符,但不包括 end 处的字符。 如果 start 与 end 相等,那么该方法返回的就是一个空串(即长度为 0 的字符串)。 如果 start 比 end 大,那么该方法在提取子串之前会先交换这两个参数。 如果 start 或 end 为负数,那么它将被替换为 0。

  例子:var str='ahji3o3s4e6p8a0sdewqdasj'

  alert(str.substring(2,6)) //结果为ji3o3

  8.函数:substr

  定义和用法 substr 方法用于返回一个从指定位置开始的指定长度的子字符串。

  语法 stringObject.substr(start [, length ])

  参数 start 必需。所需的子字符串的起始位置。字符串中的第一个字符的索引为 0。

  length 可选。在返回的子字符串中应包括的字符个数。 说明 如果 length 为 0 或负数,将返回一个空字符串。 如果没有指定该参数,则子字符串将延续到stringObject的最后。

  举例: var str = "0123456789";

  alert(str.substring(0));------------"0123456789"

  alert(str.substring(5));------------"56789"

  alert(str.substring(10));-----------""

  alert(str.substring(12));-----------""

  alert(str.substring(-5));-----------"0123456789"

  alert(str.substring(-10));----------"0123456789"

  alert(str.substring(-12));----------"0123456789"

  alert(str.substring(0,5));----------"01234"

  alert(str.substring(0,10));---------"0123456789"

  alert(str.substring(0,12));---------"0123456789"

  alert(str.substring(2,0));----------"01"

  alert(str.substring(2,2));----------""

  alert(str.substring(2,5));----------"234"

  alert(str.substring(2,12));---------"23456789"

  alert(str.substring(2,-2));---------"01"

  alert(str.substring(-1,5));---------"01234"

  alert(str.substring(-1,-5));--------""

  好了,到这里就跟大家分享完成了,如果大家还是不知道是怎么实现的,或者还存在有不理解的地方,都是可以留言咨询。

  本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处!

收起阅读 »

uni-app自定义返回逻辑教程

backbutton

自 HBuilderX v1.1.0 起,uni-app 的页面新增 onBackPress(event) 生命周期函数。

onBackPress(event) 返回 event ={from: backbutton | navigateBack}

说明

当用户进行以下操作时,会触发该函数:

  • Android 实体返回键 (from = backbutton)
  • 顶部导航栏左边的返回按钮 (from = backbutton)
  • 返回 API,即 uni.navigateBack() (from = navigateBack)

注意事项:

  • 只有在该函数中返回值为 true 时,才表示不执行默认的返回,自行处理此时的业务逻辑。
  • 当不阻止页面返回却直接调用页面路由相关接口(如:uni.switchTab)时,可能会导致页面显示异常,可以通过延迟调用路由相关接口解决。
  • 不返回或返回其它值,均会执行默认的返回行为。
  • H5 平台,顶部导航栏返回按钮支持 onBackPress(),浏览器默认返回按键及Android手机实体返回键不支持 onBackPress()
  • 暂不支持直接在自定义组件中配置该函数,目前只能是在页面中来处理。

场景示例

页面返回

场景说明:

页面中的遮罩处于显示状态时,点击返回不希望直接关闭页面,而是隐藏掉遮罩。遮罩隐藏后,继续点击返回再执行默认的逻辑。

自定义遮罩

通常自定义的遮罩/弹出层,都会做成组件,这样方便复用。

新建 uni-app 项目->components->mask.vue 文件,代码如下:

<template>  
    <view>  
        <view class="cpt-mask">  
        </view>  
    </view>  
</template>  

<script>  
    export default {}  
</script>  

<style>  
    .cpt-mask {  
        position: fixed;  
        top: 0;  
        left: 0;  
        width: 100%;  
        height: 100%;  
        background-color: #000000;  
        opacity: 0.5;  
        z-index: 99;  
    }  
</style>

引用遮罩组件

在页面中引入 mask 自定义组件后,通过一个状态值来控制其隐藏/显示。

<mask v-if="showMask"></mask>

这里用 v-if,不要用 v-show,自定义组件存在一些问题待优化。

处理返回逻辑

在 onBackPress 中,判定当前遮罩是否处于显示状态。如果显示,则关闭遮罩并返回 true。

onBackPress() {  
  if(this.showMask) {  
    this.showMask = false;  
    return true;  
  }  
},

多级返回

部分业务场景下,返回的逻辑中需要返回多级页面。

由于 uni.navigateBack() 同样会触发 onBackPress 函数。因此在 onBackPress 中直接调用 uni.navigateBack() 并始终返回 true 会引发死循环。
此时,需要根据 onBackPress 的回调对象中的 from 值来做处理,当来源是 'navigateBack' 时,返回 false。

<template>  
    <view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {};  
        },  
        onBackPress(options) {  
            if (options.from === 'navigateBack') {  
                return false;  
            }  
            this.back();  
            return true;  
        },  
        methods: {  
            back() {  
                uni.navigateBack({  
                    delta: 2  
                });  
            }  
        },  
    }  
</script>  

<style>  

</style>

应用退出

场景说明:

若在App首页,点击手机物理返回键,此时无返回页面可关闭,uni-app默认会提示“再按一次退出应用”;若想自定义退出信息,如修改为:出现一个拟态窗口提示我们是否退出应用,点击确定退出应用。点击取消,不做操作。

实现方案:

在 onBackPress 中,遮罩不显示的状态下,点击返回键将弹出拟态窗。

onBackPress() {  
   if(this.showMask) {  
     this.showMask = false;  
     return true;  
   }else{  
      uni.showModal({  
        title: '提示',  
        content: '是否退出uni-app?',  
        success: function(res) {  
            if (res.confirm) {  
                // 退出当前应用,改方法只在App中生效  
                plus.runtime.quit();  
            } else if (res.cancel) {  
                console.log('用户点击取消');  
            }  
        }  
      });  
      return true  
   }  
},

实现效果如下图:

运行体验

完整的示例代码,见附件。解压后,拖至 HBuilderX 运行即可。

继续阅读 »

自 HBuilderX v1.1.0 起,uni-app 的页面新增 onBackPress(event) 生命周期函数。

onBackPress(event) 返回 event ={from: backbutton | navigateBack}

说明

当用户进行以下操作时,会触发该函数:

  • Android 实体返回键 (from = backbutton)
  • 顶部导航栏左边的返回按钮 (from = backbutton)
  • 返回 API,即 uni.navigateBack() (from = navigateBack)

注意事项:

  • 只有在该函数中返回值为 true 时,才表示不执行默认的返回,自行处理此时的业务逻辑。
  • 当不阻止页面返回却直接调用页面路由相关接口(如:uni.switchTab)时,可能会导致页面显示异常,可以通过延迟调用路由相关接口解决。
  • 不返回或返回其它值,均会执行默认的返回行为。
  • H5 平台,顶部导航栏返回按钮支持 onBackPress(),浏览器默认返回按键及Android手机实体返回键不支持 onBackPress()
  • 暂不支持直接在自定义组件中配置该函数,目前只能是在页面中来处理。

场景示例

页面返回

场景说明:

页面中的遮罩处于显示状态时,点击返回不希望直接关闭页面,而是隐藏掉遮罩。遮罩隐藏后,继续点击返回再执行默认的逻辑。

自定义遮罩

通常自定义的遮罩/弹出层,都会做成组件,这样方便复用。

新建 uni-app 项目->components->mask.vue 文件,代码如下:

<template>  
    <view>  
        <view class="cpt-mask">  
        </view>  
    </view>  
</template>  

<script>  
    export default {}  
</script>  

<style>  
    .cpt-mask {  
        position: fixed;  
        top: 0;  
        left: 0;  
        width: 100%;  
        height: 100%;  
        background-color: #000000;  
        opacity: 0.5;  
        z-index: 99;  
    }  
</style>

引用遮罩组件

在页面中引入 mask 自定义组件后,通过一个状态值来控制其隐藏/显示。

<mask v-if="showMask"></mask>

这里用 v-if,不要用 v-show,自定义组件存在一些问题待优化。

处理返回逻辑

在 onBackPress 中,判定当前遮罩是否处于显示状态。如果显示,则关闭遮罩并返回 true。

onBackPress() {  
  if(this.showMask) {  
    this.showMask = false;  
    return true;  
  }  
},

多级返回

部分业务场景下,返回的逻辑中需要返回多级页面。

由于 uni.navigateBack() 同样会触发 onBackPress 函数。因此在 onBackPress 中直接调用 uni.navigateBack() 并始终返回 true 会引发死循环。
此时,需要根据 onBackPress 的回调对象中的 from 值来做处理,当来源是 'navigateBack' 时,返回 false。

<template>  
    <view>  
    </view>  
</template>  

<script>  
    export default {  
        data() {  
            return {};  
        },  
        onBackPress(options) {  
            if (options.from === 'navigateBack') {  
                return false;  
            }  
            this.back();  
            return true;  
        },  
        methods: {  
            back() {  
                uni.navigateBack({  
                    delta: 2  
                });  
            }  
        },  
    }  
</script>  

<style>  

</style>

应用退出

场景说明:

若在App首页,点击手机物理返回键,此时无返回页面可关闭,uni-app默认会提示“再按一次退出应用”;若想自定义退出信息,如修改为:出现一个拟态窗口提示我们是否退出应用,点击确定退出应用。点击取消,不做操作。

实现方案:

在 onBackPress 中,遮罩不显示的状态下,点击返回键将弹出拟态窗。

onBackPress() {  
   if(this.showMask) {  
     this.showMask = false;  
     return true;  
   }else{  
      uni.showModal({  
        title: '提示',  
        content: '是否退出uni-app?',  
        success: function(res) {  
            if (res.confirm) {  
                // 退出当前应用,改方法只在App中生效  
                plus.runtime.quit();  
            } else if (res.cancel) {  
                console.log('用户点击取消');  
            }  
        }  
      });  
      return true  
   }  
},

实现效果如下图:

运行体验

完整的示例代码,见附件。解压后,拖至 HBuilderX 运行即可。

收起阅读 »

HBuilderX 在server 2008 64x中使用中,突然闪退 代码消失找不回

HBuilderX 在server 2008 64x中使用中,突然闪退 代码消失找不回

当前编辑代码消盟,直接找不回,大家小心使用本软件!

HBuilderX 在server 2008 64x中使用中,突然闪退 代码消失找不回

当前编辑代码消盟,直接找不回,大家小心使用本软件!

vue h5+app蓝牙打印小票

蓝牙

参考网址:

链接蓝牙:https://blog.csdn.net/cxgasd/article/details/78208708

蓝牙打印机指令:https://www.jianshu.com/p/c0b6d1a4823b

1,打印出来的文本是字节流,平常的换行,样式都用不了

2,一行字母有32个字符,一行全部 中文有16个字符 ,1中文=2字母 ,js判断字符串个数

strLen: function(val) {  
    var len = 0;  
    for (var i = 0; i < val.length; i++) {  
        var a = val.charAt(i);  
        if (a.match(/[^\x00-\xff]/ig) != null) {  
            len += 1;  
        } else {  
            len += 0.5;  
        }  
    }  
    return len;  
}

实现字符串 拼接的方法: 左右对齐是中间填空格,隔行是一行32个空格

  1. 蓝牙打印机指令 可以实现 文字放大,图案

    btOutStream.write(0x1B);
    btOutStream.write(0x40);
    //或者
    // btOutStream.write([0x1B,0x40]);

//打印小票
printComponent:{

    //字符个数  
    strLen:function(val) {  
      var len = 0;  
      for (var i = 0; i < val.length; i++) {  
        var a= val.charAt(i);  
        if (a.match(/[^\x00-\xff]/ig) != null) {  
          len += 1;  
        }else {  
          len += 0.5;  
        }  
      }  
      return len;  
    },  

    //空格  
    spacing:function(str){  
       var blank="";  
       var row=Math.ceil(this.strLen(str)/16); //行数  
       var len=(16*row-this.strLen(str))*2;  
       for(var i=0;i<len;i++){  
            blank+=' ';  
       }  
       console.log((str+blank).length);  
       return str+blank;  
    },  

    //左右对齐  
    justify:function(str1,str2){  
       var blank="";  
       var len=(16-this.strLen(str1)-this.strLen(str2))*2;  
       for(var i=0;i<len;i++){  
            blank+=' ';  
       }  
       return str1+blank+str2;  
    },  

    //分割线  
    newline:function(){  
      var line="--------------------------------";  
      return line;  
    },  

    //打印(拣货单)  
    print3:function(_this){   
       var str='';  
       //str+='   —YOULENONG—  ';  
       str+=this.spacing(' ');  
       str+=this.spacing('订单号:'+_this.order_no);  

       for(var i=0;i<_this.goodsArr.length;i++){  
           var v=_this.goodsArr[i];  
           str+=this.spacing(v.goods_name);  
           str+=this.justify('销价:'+v.goods_price+'/'+v.unit,'购买量:'+v.goods_num);  
           str+=this.spacing('货号:'+v.product_no);  
           //str+=this.justify('折扣:-xxx','折后:xxx');  
           str+=this.newline();  
       }  
       str+=this.spacing(new Date().Format('MM-dd hh:mm:ss'));  
       str+=this.spacing(' ');  
       str+=this.spacing(' ');  
       str+=this.spacing(' ');  
       _this.bluetoothTool.sendData(str);  //把字符串 发给蓝牙,打印  
    }  

}

附件有代码,我删减了,不保证运行成功,得看懂vue

继续阅读 »

参考网址:

链接蓝牙:https://blog.csdn.net/cxgasd/article/details/78208708

蓝牙打印机指令:https://www.jianshu.com/p/c0b6d1a4823b

1,打印出来的文本是字节流,平常的换行,样式都用不了

2,一行字母有32个字符,一行全部 中文有16个字符 ,1中文=2字母 ,js判断字符串个数

strLen: function(val) {  
    var len = 0;  
    for (var i = 0; i < val.length; i++) {  
        var a = val.charAt(i);  
        if (a.match(/[^\x00-\xff]/ig) != null) {  
            len += 1;  
        } else {  
            len += 0.5;  
        }  
    }  
    return len;  
}

实现字符串 拼接的方法: 左右对齐是中间填空格,隔行是一行32个空格

  1. 蓝牙打印机指令 可以实现 文字放大,图案

    btOutStream.write(0x1B);
    btOutStream.write(0x40);
    //或者
    // btOutStream.write([0x1B,0x40]);

//打印小票
printComponent:{

    //字符个数  
    strLen:function(val) {  
      var len = 0;  
      for (var i = 0; i < val.length; i++) {  
        var a= val.charAt(i);  
        if (a.match(/[^\x00-\xff]/ig) != null) {  
          len += 1;  
        }else {  
          len += 0.5;  
        }  
      }  
      return len;  
    },  

    //空格  
    spacing:function(str){  
       var blank="";  
       var row=Math.ceil(this.strLen(str)/16); //行数  
       var len=(16*row-this.strLen(str))*2;  
       for(var i=0;i<len;i++){  
            blank+=' ';  
       }  
       console.log((str+blank).length);  
       return str+blank;  
    },  

    //左右对齐  
    justify:function(str1,str2){  
       var blank="";  
       var len=(16-this.strLen(str1)-this.strLen(str2))*2;  
       for(var i=0;i<len;i++){  
            blank+=' ';  
       }  
       return str1+blank+str2;  
    },  

    //分割线  
    newline:function(){  
      var line="--------------------------------";  
      return line;  
    },  

    //打印(拣货单)  
    print3:function(_this){   
       var str='';  
       //str+='   —YOULENONG—  ';  
       str+=this.spacing(' ');  
       str+=this.spacing('订单号:'+_this.order_no);  

       for(var i=0;i<_this.goodsArr.length;i++){  
           var v=_this.goodsArr[i];  
           str+=this.spacing(v.goods_name);  
           str+=this.justify('销价:'+v.goods_price+'/'+v.unit,'购买量:'+v.goods_num);  
           str+=this.spacing('货号:'+v.product_no);  
           //str+=this.justify('折扣:-xxx','折后:xxx');  
           str+=this.newline();  
       }  
       str+=this.spacing(new Date().Format('MM-dd hh:mm:ss'));  
       str+=this.spacing(' ');  
       str+=this.spacing(' ');  
       str+=this.spacing(' ');  
       _this.bluetoothTool.sendData(str);  //把字符串 发给蓝牙,打印  
    }  

}

附件有代码,我删减了,不保证运行成功,得看懂vue

收起阅读 »

HTML编程之滚轮滑动监听效果的实现

在前端页面制作过程中,我们经常会发现有时候某些动作需要滚动滑轮来实现,那么对于初学者来说可能不明白是如何实现的,对于这个问题,下面就来跟大家分析一下,如何用滚轮滑动监听效果的实现。

源代码如下:

windowAddMouseWheel();
function windowAddMouseWheel() {
var scrollFunc = function (e) {
e = e || window.event;
if (e.wheelDelta) { //判断浏览器IE,谷歌滑轮事件
if (e.wheelDelta > 0) { //当滑轮向上滚动时
alert("滑轮向上滚动");
}
if (e.wheelDelta < 0) { //当滑轮向下滚动时
alert("滑轮向下滚动");
}
} else if (e.detail) { //Firefox滑轮事件
if (e.detail> 0) { //当滑轮向上滚动时
alert("滑轮向上滚动");
}
if (e.detail< 0) { //当滑轮向下滚动时
alert("滑轮向下滚动");
}
}
};
//给页面绑定滑轮滚动事件
if (document.addEventListener) {
document.addEventListener('DOMMouseScroll', scrollFunc, false);
}
//滚动滑轮触发scrollFunc方法
window.onmousewheel = document.onmousewheel = scrollFunc;
}

现在大家知道是如何控制实现的吧,如果还是有不明白的地方,或者不知道哪里出错了,都是可以留言咨询我们来寻求帮助和解答的。
本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处。

继续阅读 »

在前端页面制作过程中,我们经常会发现有时候某些动作需要滚动滑轮来实现,那么对于初学者来说可能不明白是如何实现的,对于这个问题,下面就来跟大家分析一下,如何用滚轮滑动监听效果的实现。

源代码如下:

windowAddMouseWheel();
function windowAddMouseWheel() {
var scrollFunc = function (e) {
e = e || window.event;
if (e.wheelDelta) { //判断浏览器IE,谷歌滑轮事件
if (e.wheelDelta > 0) { //当滑轮向上滚动时
alert("滑轮向上滚动");
}
if (e.wheelDelta < 0) { //当滑轮向下滚动时
alert("滑轮向下滚动");
}
} else if (e.detail) { //Firefox滑轮事件
if (e.detail> 0) { //当滑轮向上滚动时
alert("滑轮向上滚动");
}
if (e.detail< 0) { //当滑轮向下滚动时
alert("滑轮向下滚动");
}
}
};
//给页面绑定滑轮滚动事件
if (document.addEventListener) {
document.addEventListener('DOMMouseScroll', scrollFunc, false);
}
//滚动滑轮触发scrollFunc方法
window.onmousewheel = document.onmousewheel = scrollFunc;
}

现在大家知道是如何控制实现的吧,如果还是有不明白的地方,或者不知道哪里出错了,都是可以留言咨询我们来寻求帮助和解答的。
本文由专业的郑州app开发公司燚轩科技整理发布,原创不易,如需转载请注明出处。

收起阅读 »

hbuilder开发H5应用时,清除了ios的角标。但再次收到个推推送时角标还是原数字+1。

个推 iOS

直接从应用setBadgeNumber(0),只是清除了应用本地的角标。但是个推对于当前clientid还有一个记录。再次推送的时候,会自己+1并把数字推过来。
所以,清除了本地的数字,还要通知个推也清除。

try{

plus.runtime.setBadgeNumber(0);

if(mui.os.ios){

var GeTuiSdk = plus.ios.importClass('GeTuiSdk');  

GeTuiSdk.setBadge(0);  

}

console.log("清除角标")

}catch(e){

console.log("清除角标异常")

}

继续阅读 »

直接从应用setBadgeNumber(0),只是清除了应用本地的角标。但是个推对于当前clientid还有一个记录。再次推送的时候,会自己+1并把数字推过来。
所以,清除了本地的数字,还要通知个推也清除。

try{

plus.runtime.setBadgeNumber(0);

if(mui.os.ios){

var GeTuiSdk = plus.ios.importClass('GeTuiSdk');  

GeTuiSdk.setBadge(0);  

}

console.log("清除角标")

}catch(e){

console.log("清除角标异常")

}

收起阅读 »

HBuilderX如何保护程序员的身心健康

绿柔 鼠标手

> 本帖已集成到: hx产品文档

键盘、椅子有人体工学,一个开发工具软件,有什么健不健康的概念呢?
当然有的。你的眼睛、手的健康,都与开发工具有很大关系。
DCloud是一家有匠心、有情怀的团队,在工具对人体的健康影响方面投入了大量精力财力研究。

护眼:绿柔环保主题

很多人误以为黑色主题最护眼,我们自己也曾长时间使用黑色主题,但感受不对劲,于是较真的研究了到底什么是健康护眼的主题。
其实参考电子书领域就知道,长时间盯着电脑屏幕或手机屏,背景绝不能是黑色,而应该是泛黄的颜色。如果一家做电子书的公司使用黑色背景的话,这公司肯定会被读者抛弃掉。

但是电子书行业为什么普遍采用泛黄色呢?是为了给读者制造看纸书的错觉?还是真有科学道理。我们需要仔细研究。

有一个误传的说法是:黑色背景,在液晶下发光量少,从而更护眼。
由于纯黑色背景很难看,实际上各个开发工具的背景色不是纯黑色,液晶面板上的粒子仍然在发光。
虽然液晶面板的暗色区光线量低,但就人眼肌肉的紧张度而言,高对比度的伤害大于光线量的伤害。
在阳光明媚的草原上,虽然光线量非常大,但人眼很舒适。
但在黑色屋子里,手机屏幕亮度调到最低,光线量远低于前者,但仍然刺眼。
这是对比度的影响造成的。

HBuilder的默认主题是绿柔,其特点是柔和、低对比度、强光下仍清晰、绿色感加强。对着这样的界面写一天代码,感受要比对着太亮或太暗的界面舒服很多。
绿柔是一个严谨的设计过程,包含色彩心理学和色彩生理学的很多知识,并且在北京国奥心理医院做过严谨的医学测试。
我们安排工程师在不同的配色主题下写代码,在医院检测他们的脑疲劳程度,最终调校出了绿柔配色方案,实验过程如下:

> 参与实验的2位程序员在充分休息后,分别使用绿柔和黑色主题的编程工具进行编码半小时,并监测脑电波的数据变化。
> 通过对实验者的脑电波产生的17万条数据的分析,利用医院仪器打印出的疲劳值、紧张度和注意力集中程度数值,发现实验结论如下:
> 半小时编码后2位测试者的疲劳度均上升,但,绿柔主题下工作的程序员疲劳值上升相对缓慢,紧张程度更低,注意力更集中。
> 使用黑色主题编码后疲劳值的上升幅度最高达到使用绿柔上升幅度的700%!
> 此实验进行多次,并有交叉测试,结论始终相同。


上图为在医院参与实验的程序员。

色彩方面有色彩生理学和色彩心理学。我们先从色彩生理学角度看看黑色主题的问题:

  1. 转场晕眩
    当一个人眼睛长期看暗色系事物时,大脑会分泌激素强迫眼睛增强视力。
    比如我们在一个黑屋子待一会就能看清东西,但回到阳光下时又会晕眩,这叫做暗适应。
    同样,长期看黑色的代码区,此时切换到其他软件界面或看窗外就会晕眩。
  2. 夜盲症
    长期的黑暗环境下眼球会分泌视紫红质,视紫红质消耗维生素A,并且会引发夜盲症,虽不是近视,但夜晚视力低于常人。
    具体见百度百科视紫红质
  3. 对比度高
    黑色背景往往对应高对比度的前景内容,而高对比度不适合长期观看。
    其实就像糖和辣都不能多吃,但水可以多喝。
    而过于柔和的画面,在强光照射或视力不佳时,可能无法保持足够的清晰度,绿柔反复调校参数才得到目前的结果。

我们查阅资料得知,人眼最喜欢的光是黄色和绿色
黄色和绿色的波长,是人眼最放松的波长。
绿柔的背景是暖黄色,界面中点缀不少绿色,前景的代码颜色,虽然多彩,但每种颜色的RGB中的G,即绿色值,都调大了。

另外从色彩心理学看,温暖的颜色比暗色更舒服。
人眼看到的景色,大脑会产生联想,并进而影响心情。
看到绿色就会联想到草原森林,心情不自觉的就好一点。
而看久了暗色或刺激性颜色,心情会压抑。
每天看8个小时屏幕不是小事,心情舒畅很重要。

"群体无小事,长期无小事",面对数百万开发者,我们必须下足功夫研究如何保护他们的健康。
结合了众多理论和实验,我们终于打磨出了绿柔主题。
有些产品的设计原则是漂亮优先,HX不是这个原则,是健康优先。HX可能不是最漂亮的编辑器,但肯定是最健康的编辑器。

护手:免拖动选择保护手指关节神经

鼠标手是程序员常见病,在鼠标操作时,什么操作劳损最严重?答案是拖选。
拖选过程是这样:食指按下鼠标左键,神经保持紧张,移动手腕带动鼠标移动,选择内容,松开食指。这个过程中,食指持续按下,相连的肌肉神经一直处于紧绷状态。
如果使用触摸板那更痛苦。
还是"偶尔无大事,长期无小事"的逻辑,偶尔拖选不觉得怎么样,但一天8小时写代码下来,手部劳损主因其实就是拖选。
为了减少拖选,HX提供了快捷键选择、智能双击选择、免选择直接操作等模式。

  • 快捷键选择
    Ctrl+=是以当前光标为初始点扩大选区,Ctrl+E是选择当前词或相同词,还有众多选择快捷键,具体见菜单-选择。
  • 智能双击选择
    HX在很多地方双击,会有特殊选择逻辑,在行首、行尾、函数块、等号、逗号、-号、括号、引号、行首列表符、<!-- -->等特殊语法符号处双击,都能快速选择相应内容。
    掌握这套智能双击逻辑,即能极大的提高操作效率,又能减少手部劳损。
    具体智能双击列表,见菜单选择。强烈建议认真研究掌握智能双击。
  • 免选择直接操作
    如果没有选内容,直接按Ctrl+C或X进行复制和剪切,可以对光标所在行复制剪切。
    Ctrl+Insert重复选区、Ctrl+Shift+X交换选区等很多操作,也都在无选区时自动适配相应行操作。
    Ctrl+D删除行、Ctrl+上下移动行、Shift+Del删除到行尾等操作也可以无选择直接操作文本内容。
    更多功能见菜单编辑。

作为为开发者提供服务的公司,尤其是我们自己也是开发者,我们最重视的就是高效率和健康。
希望通过我们的努力,可以让中国数百万开发者的开发效率更高,身心更健康。

继续阅读 »

> 本帖已集成到: hx产品文档

键盘、椅子有人体工学,一个开发工具软件,有什么健不健康的概念呢?
当然有的。你的眼睛、手的健康,都与开发工具有很大关系。
DCloud是一家有匠心、有情怀的团队,在工具对人体的健康影响方面投入了大量精力财力研究。

护眼:绿柔环保主题

很多人误以为黑色主题最护眼,我们自己也曾长时间使用黑色主题,但感受不对劲,于是较真的研究了到底什么是健康护眼的主题。
其实参考电子书领域就知道,长时间盯着电脑屏幕或手机屏,背景绝不能是黑色,而应该是泛黄的颜色。如果一家做电子书的公司使用黑色背景的话,这公司肯定会被读者抛弃掉。

但是电子书行业为什么普遍采用泛黄色呢?是为了给读者制造看纸书的错觉?还是真有科学道理。我们需要仔细研究。

有一个误传的说法是:黑色背景,在液晶下发光量少,从而更护眼。
由于纯黑色背景很难看,实际上各个开发工具的背景色不是纯黑色,液晶面板上的粒子仍然在发光。
虽然液晶面板的暗色区光线量低,但就人眼肌肉的紧张度而言,高对比度的伤害大于光线量的伤害。
在阳光明媚的草原上,虽然光线量非常大,但人眼很舒适。
但在黑色屋子里,手机屏幕亮度调到最低,光线量远低于前者,但仍然刺眼。
这是对比度的影响造成的。

HBuilder的默认主题是绿柔,其特点是柔和、低对比度、强光下仍清晰、绿色感加强。对着这样的界面写一天代码,感受要比对着太亮或太暗的界面舒服很多。
绿柔是一个严谨的设计过程,包含色彩心理学和色彩生理学的很多知识,并且在北京国奥心理医院做过严谨的医学测试。
我们安排工程师在不同的配色主题下写代码,在医院检测他们的脑疲劳程度,最终调校出了绿柔配色方案,实验过程如下:

> 参与实验的2位程序员在充分休息后,分别使用绿柔和黑色主题的编程工具进行编码半小时,并监测脑电波的数据变化。
> 通过对实验者的脑电波产生的17万条数据的分析,利用医院仪器打印出的疲劳值、紧张度和注意力集中程度数值,发现实验结论如下:
> 半小时编码后2位测试者的疲劳度均上升,但,绿柔主题下工作的程序员疲劳值上升相对缓慢,紧张程度更低,注意力更集中。
> 使用黑色主题编码后疲劳值的上升幅度最高达到使用绿柔上升幅度的700%!
> 此实验进行多次,并有交叉测试,结论始终相同。


上图为在医院参与实验的程序员。

色彩方面有色彩生理学和色彩心理学。我们先从色彩生理学角度看看黑色主题的问题:

  1. 转场晕眩
    当一个人眼睛长期看暗色系事物时,大脑会分泌激素强迫眼睛增强视力。
    比如我们在一个黑屋子待一会就能看清东西,但回到阳光下时又会晕眩,这叫做暗适应。
    同样,长期看黑色的代码区,此时切换到其他软件界面或看窗外就会晕眩。
  2. 夜盲症
    长期的黑暗环境下眼球会分泌视紫红质,视紫红质消耗维生素A,并且会引发夜盲症,虽不是近视,但夜晚视力低于常人。
    具体见百度百科视紫红质
  3. 对比度高
    黑色背景往往对应高对比度的前景内容,而高对比度不适合长期观看。
    其实就像糖和辣都不能多吃,但水可以多喝。
    而过于柔和的画面,在强光照射或视力不佳时,可能无法保持足够的清晰度,绿柔反复调校参数才得到目前的结果。

我们查阅资料得知,人眼最喜欢的光是黄色和绿色
黄色和绿色的波长,是人眼最放松的波长。
绿柔的背景是暖黄色,界面中点缀不少绿色,前景的代码颜色,虽然多彩,但每种颜色的RGB中的G,即绿色值,都调大了。

另外从色彩心理学看,温暖的颜色比暗色更舒服。
人眼看到的景色,大脑会产生联想,并进而影响心情。
看到绿色就会联想到草原森林,心情不自觉的就好一点。
而看久了暗色或刺激性颜色,心情会压抑。
每天看8个小时屏幕不是小事,心情舒畅很重要。

"群体无小事,长期无小事",面对数百万开发者,我们必须下足功夫研究如何保护他们的健康。
结合了众多理论和实验,我们终于打磨出了绿柔主题。
有些产品的设计原则是漂亮优先,HX不是这个原则,是健康优先。HX可能不是最漂亮的编辑器,但肯定是最健康的编辑器。

护手:免拖动选择保护手指关节神经

鼠标手是程序员常见病,在鼠标操作时,什么操作劳损最严重?答案是拖选。
拖选过程是这样:食指按下鼠标左键,神经保持紧张,移动手腕带动鼠标移动,选择内容,松开食指。这个过程中,食指持续按下,相连的肌肉神经一直处于紧绷状态。
如果使用触摸板那更痛苦。
还是"偶尔无大事,长期无小事"的逻辑,偶尔拖选不觉得怎么样,但一天8小时写代码下来,手部劳损主因其实就是拖选。
为了减少拖选,HX提供了快捷键选择、智能双击选择、免选择直接操作等模式。

  • 快捷键选择
    Ctrl+=是以当前光标为初始点扩大选区,Ctrl+E是选择当前词或相同词,还有众多选择快捷键,具体见菜单-选择。
  • 智能双击选择
    HX在很多地方双击,会有特殊选择逻辑,在行首、行尾、函数块、等号、逗号、-号、括号、引号、行首列表符、<!-- -->等特殊语法符号处双击,都能快速选择相应内容。
    掌握这套智能双击逻辑,即能极大的提高操作效率,又能减少手部劳损。
    具体智能双击列表,见菜单选择。强烈建议认真研究掌握智能双击。
  • 免选择直接操作
    如果没有选内容,直接按Ctrl+C或X进行复制和剪切,可以对光标所在行复制剪切。
    Ctrl+Insert重复选区、Ctrl+Shift+X交换选区等很多操作,也都在无选区时自动适配相应行操作。
    Ctrl+D删除行、Ctrl+上下移动行、Shift+Del删除到行尾等操作也可以无选择直接操作文本内容。
    更多功能见菜单编辑。

作为为开发者提供服务的公司,尤其是我们自己也是开发者,我们最重视的就是高效率和健康。
希望通过我们的努力,可以让中国数百万开发者的开发效率更高,身心更健康。

收起阅读 »

uni-app nvue沉浸式状态栏(线性渐变色)

<template>  
<div class="page-searchList">  
    <div class="header">  
        <div class="statusBar" :style="{height:statusBarHeight+'wx'}"></div><!-- 状态栏占位 -->  
        <div class="info"><text style="font-size: 16wx;">标题栏</text></div>  
    </div>  
    <div class="content">  
        <text style="font-size: 50wx;">{{statusBarHeight}}wx</text> <!-- 打印状态栏高度 -->  
    </div>  
</div>  
</template>  

<script>  
export default {  
    data: {  
        statusBarHeight: ''  
    },  
    created() {  
        let _t = this;  
        setTimeout(() => { //获取状态栏高度,setTimeout后才能调用uni.  
            uni.getSystemInfo({  
                success: function(res) {  
                    _t.statusBarHeight = res.statusBarHeight;  
                    console.log(_t.statusBarHeight);  
                }  
            });  
        }, 1);  
    }  
};  
</script>  

<style>  
.page-searchList {  
    background-color: #8f8f94;  
    justify-content: center;  
    align-items: center;  
}  
.header {  
    background-image: linear-gradient(to right, #a80077, #66ff00);  
    width: 750px;  
    position: fixed;  
    top: 0;  
}  
.info {  
    height: 44wx;  
    justify-content: center;  
    align-items: center;  
}  
</style>

继续阅读 »
<template>  
<div class="page-searchList">  
    <div class="header">  
        <div class="statusBar" :style="{height:statusBarHeight+'wx'}"></div><!-- 状态栏占位 -->  
        <div class="info"><text style="font-size: 16wx;">标题栏</text></div>  
    </div>  
    <div class="content">  
        <text style="font-size: 50wx;">{{statusBarHeight}}wx</text> <!-- 打印状态栏高度 -->  
    </div>  
</div>  
</template>  

<script>  
export default {  
    data: {  
        statusBarHeight: ''  
    },  
    created() {  
        let _t = this;  
        setTimeout(() => { //获取状态栏高度,setTimeout后才能调用uni.  
            uni.getSystemInfo({  
                success: function(res) {  
                    _t.statusBarHeight = res.statusBarHeight;  
                    console.log(_t.statusBarHeight);  
                }  
            });  
        }, 1);  
    }  
};  
</script>  

<style>  
.page-searchList {  
    background-color: #8f8f94;  
    justify-content: center;  
    align-items: center;  
}  
.header {  
    background-image: linear-gradient(to right, #a80077, #66ff00);  
    width: 750px;  
    position: fixed;  
    top: 0;  
}  
.info {  
    height: 44wx;  
    justify-content: center;  
    align-items: center;  
}  
</style>

收起阅读 »