前端使用浏览器实现asr语音识别


// 创建一个新的SpeechRecognition对象 var recognition = new (window.SpeechRecognition || window.webkitSpeechRecognition || window.mozSpeechRecognition || window.msSpeechRecognition)(); recognition.lang = 'en-US'; // 设置语言 recognition.interimResults = false; // 设置是否返回临时结果 recognition.maxAlternatives = 1; // 设置返回的最大替代词数量 // 当识别到语音结束时,返回结果 recognition.onresult = function(event) { console.log('You said: ', event.results[0][0].transcript); }; // 开始语音识别 recognition.start();

demo

 

react-native仿微信通讯录右侧边栏快速定位功能

代码地址 -> ReactNativeCountrySelect

1. 界面

SectionList把数据渲染出来

右边的A-ZText组件即可,这里为了做滑动定位,没有选择Touchable组件

import * as React from 'react';
import {
  Text,
  View,
  StyleSheet,
  SectionList,
  SafeAreaView,
} from 'react-native';

import countries from './countryCode.json';
const sectionMapArr = [
  ['A', 0],
  ['B', 1],
  ['C', 2],
  ['D', 3],
  ['E', 4],
  ['F', 5],
  ['G', 6],
  ['H', 7],
  ['I', 8],
  ['J', 9],
  ['K', 10],
  ['L', 11],
  ['M', 12],
  ['N', 13],
  ['O', 14],
  ['P', 15],
  ['Q', 16],
  ['R', 17],
  ['S', 18],
  ['T', 19],
  ['U', 20],
  ['V', 21],
  ['W', 22],
  ['X', 23],
  ['Y', 24],
  ['Z', 25],
];
export default class App extends React.Component {
  render() {
    return (
      <SafeAreaView style={styles.container}>
        <SectionList
          containerStyle={{ flex: 1, justifyContent: 'center' }}
          ItemSeparatorComponent={() => (<View style={{ borderBottomColor: '#F8F8F8', borderBottomWidth: 1, }} />)}
          renderItem={({ item, index, section }) => (
            <View style={styles.itemContainer}>
              <Text style={styles.itemText} key={index}>
                {item.countryName}
              </Text>
            </View>
          )}
          renderSectionHeader={({ section: { key } }) => (
            <View style={styles.headerContainer}>
              <Text style={styles.headerText}>{key}</Text>
            </View>
          )}
          sections={countries}
          keyExtractor={(item, index) => item + index}
        />
        <View
          style={{ width: 16, justifyContent: 'center' }}
        >
          {sectionMapArr.map((item, index) => {
            return (
              <Text
                key={index}
              >
                {item[0]}
              </Text>
            );
          })}
        </View>
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#FFFFFF',
    flexDirection: 'row',
  },
  headerContainer: {
    padding: 5,
    backgroundColor: '#F8F8F8',
  },
  headerText: {
    fontWeight: 'bold',
  },
  itemContainer: {
    paddingHorizontal: 5,
    paddingVertical: 10,
  },
  itemText: {
    fontSize: 16,
  },
});

这时候界面已经完成了,然后就是增加触摸滑动定位的功能了。

2. 使用Gesture Responder System监听触摸事件

给右侧的View上启用手势


<View style={{ width: 16, justifyContent: 'center' }} onStartShouldSetResponder={() => true} onMoveShouldSetResponder={() => true} > {sectionMapArr.map((item, index) => { return ( <Text key={index} > {item[0]} </Text> ); })} </View>

这样我们就可以在触摸滑动的时候,获得滑动到的位置

3. 使用onLayout找到每个字母对应的X、Y

首先在constructor里声明一个实例属性,用来记录每个字母的信息:

this.ps = [];

然后在Text组件上利用onLayout获得每个字母的位置,并且存到this.ps里:

<Text
  key={index}
  onLayout={({
    nativeEvent: {
      layout: { x, y, width, height },
    },
  }) => {
    this.ps = this.ps.filter(i => i.key !== item[0]); 
    this.ps.push({
      key: item[0],     // 对应的字母 A-Z
      min: y,           // 字母顶部Y坐标
      max: y + height,  // 字母底部Y坐标
      index: item[1],   // 字母对应SectionList的index
    });
  }}
>
  {item[0]}
</Text>

4. 根据滑动找到滑到哪个字母上

<View
  style={{ width: 16, justifyContent: 'center' }}
  onStartShouldSetResponder={() => true}
  onMoveShouldSetResponder={() => true}
  onResponderMove={({ nativeEvent: { pageY } }) => {
    const offsetY = pageY - this.offsetY;
    const find = this.ps.find(
      i => i.min < offsetY && i.max > offsetY
    );
    if (find) {
      console.log(find) // 滑动到的字母
    }
  }}
>

5. 根据触摸的字母,SectionList跳到对应的位置

  1. 先在constructor里创建ref
this.sectionlist = React.createRef();
  1. 在SectionList上绑定ref
ref={this.sectionlist}
  1. 调用SectionList的scrollToLocation
onResponderMove={({ nativeEvent: { pageY } }) => {
  const offsetY = pageY - this.offsetY;
  const find = this.ps.find(
    i => i.min < offsetY && i.max > offsetY
  );
  if (find) {
    this.sectionlist.current.scrollToLocation({
      sectionIndex: find.index,
      itemIndex: 0,
      animated: false,
    });
  }
}}

完工

react native改变WebView背景颜色

react-native的WebView背景颜色是无法改变的,不过我们可以用另一个View盖住WebView,达到改变颜色的效果。

import React from 'react';
import {View, StyleSheet, WebView, ActivityIndicator} from 'react-native';

export default class YourComponent extends React.PureComponent{

    state = {
        loading: true,
    };

    render() {
        return (
            <View>
                <WebView
                    source={html}
                    onLoadEnd={() => {
                        this.setState({loading: false});
                    }}
                />
                {
                    this.state.loading && (
                        <View style={{
                            ...StyleSheet.absoluteFillObject,
                            backgroundColor: '#000000',  // your color 
                            alignItems: 'center',
                            justifyContent: 'center',
                        }}>
                            <ActivityIndicator />
                        </View>
                    )
                }
            </View>
        )

    }
}

Chrome中from memory cache和from disk cache

Chrome中文件缓存有Memory Cache和Disk Cache两种

顾名思义

  • Memory Cache:放在内存中的缓存

  • Disk Cache:放在硬盘上的缓存

一些规律:

  1. 第一次打开页面的时候,是没有缓存的,直接请求资源

  2. 刷新页面(⌘+R)的时候,会发现有些文件是from memory cache,有些是from disk cache

  3. 关掉浏览器,再打开页面,没有memory cache了

  4. 无痕窗口下,资源都会放在memory cache,关掉窗口缓存就没了,不会留下痕迹

  5. 图片会优先放进memory cache

  6. 小文件会优先放进memory cache

  7. 大文件几乎是disk cache

最后送上一张http缓存的图片

iOS开发:在Swift中使用Alamofire发送HTTP请求

创建项目

  1. 打开Xcode,点击Create a new Xcode project

  2. 选择Single View App,点击Next

  3. 输入Product Name:demo,Language选择Swift,点击Next

  4. 选择一个目录存放你的项目

使用Pod安装Alamofire

  1. 关掉Xcode

  2. 在你的项目目录里,创建一个文件Podfile

  3. 输入

    source 'https://github.com/CocoaPods/Specs.git'
    platform :ios, '10.0'
    use_frameworks!
    
    target 'demo' do
        pod 'Alamofire', '~> 4.7'
    end
    
  4. 在终端中输入:pod install

  5. 打开demo目录,双击demo.xcworkspace打开项目

使用Alamofire发送HTTP请求

  1. 打开文件ViewController.swift

  2. 在viewDidLoad函数内调用Alamofire

    Alamofire.request("https://api.github.com/gists").responseJSON { response in
        print("Request: \(String(describing: response.request))")   // original url request
        print("Response: \(String(describing: response.response))") // http url response
        print("Result: \(response.result)")                         // response serialization result
    
        if let json = response.result.value {
            print("JSON: \(json)") // serialized json response
        }
    
        if let data = response.data, let utf8Text = String(data: data, encoding: .utf8) {
            print("Data: \(utf8Text)") // original server data as UTF8 string
        }
    }
    
  3. ⌘+R启动APP,可以看到输出

使用Gatsby生成静态网站并部署在GitHub上

什么是Gatsby

Blazing-fast static site generator for React

Gatsby是一个基于React极其快的静态网站生成工具

支持各种数据源,markdown、Wordpress等

创建网站

  1. 安装Gatsby命令行工具:
    npm install --global gatsby-cli
    
  2. 创建一个新的网站
    gatsby new sheng00.cn
    
  3. 运行刚才创建的网站
    cd sheng00.cn
    gatsby develop
    

    打开localhost:8000即可看到

部署在GitHub上

  1. 在GitHub上创建一个repository

  2. 在刚才生成的网站运行下面的命令

    git add -A
    git commit -m "first commit"
    git remote add origin git@github.com:shengoo/sheng00.cn.git
    git push -u origin master
    
  3. 安装gh-pages
    yarn add gh-pages
    
  4. 在package.json里增加一个脚本
    "deploy": "gatsby build --prefix-paths && gh-pages -d public"
    
  5. 部署到GitHub
    yarn deploy
    

设置自定义域名

  1. 从域名注册商那里,把域名指向yourusername.github.io

  2. 在GitHub的repository的设置里,设置Custom Domain:www.sheng00.cn

  3. 在Gatsby生成的网站中,新建一个目录static,创建一个文件CNAME

    www.sheng00.cn
    
  4. 再次部署
    yarn deploy
    

源代码网址:https://github.com/shengoo/sheng00.cn

在react应用中使用模块化css

什么是模块化CSS?

模块化管理CSS,避免全局污染,实现模块化、可服用。

在react中应用

启用CSS Modules

css-loader中增加一个选项:

{
    loader: require.resolve('css-loader'),
    options: {
        importLoaders: 1,
        minimize: true,
        sourceMap: shouldUseSourceMap,
        modules: true, // 启用CSS Modules
    },
},

CSS文件

app.css:

.title{
    color: red;
}

最后会编译成:

._2L1SLeGPg5sisdRmqO9mCH {
  color: red;
}

JavaScript文件

app.js:

import styles from './app.css';

class App extends Component {
  render() {
    return <div className={styles.title}>Hello World.</div>;
  }
}

最后会编译成:

<div class="_2L1SLeGPg5sisdRmqO9mCH">Hello World.</div>

fetch 用法简介

Fetch API提供了一个获取资源的接口(包括跨域)。任何使用过 XMLHttpRequest 的人都能轻松上手,但新的API提供了更强大和灵活的功能集。

参数

第一个参数是URL地址。

第二个参数(可选)是fetch使用的选项(method、headers等选项)

返回结果

fetch会返回一个promise对象,resolve对应请求的Response。

response属性:

  1. body
  2. bodyUsed

    一个布尔值来标示该Response是否读取过Body

  3. headers

    此Response所关联的Headers 对象.

  4. ok

    一个布尔值来标示该Response成功(状态码200-299) 还是失败.

  5. redirected

    该Response是否来自一个重定向,如果是的话,它的URL列表将会有多个

  6. status

    Response的状态码 (例如, 200 成功).

  7. statusText

    与该Response状态码一致的状态信息 (例如, OK对应200).

  8. type

    Response的类型 (例如, basic, cors).

  9. url

    Response的URL.

response方法

  1. arrayBuffer()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为ArrayBuffer格式的promise对象

  2. blob()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为Blob格式的promise对象

  3. formData()

    读取Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为FormData格式的promise对象

  4. json()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为JSON格式的promise对象

  5. text()

    读取 Response对象并且将它设置为已读(因为Responses对象被设置为了 stream 的方式,所以它们只能被读取一次) ,并返回一个被解析为USVString格式的promise对象

示例:读取一个图片并生成URL

var myImage = document.querySelector('.my-image');
fetch('flowers.jpg').then(function(response) {
  return response.blob();
}).then(function(response) {
  var objectURL = URL.createObjectURL(response);
  myImage.src = objectURL;
});

get请求

fetch('http://example.com/movies.json')
  .then(function(response) {
    return response.json();
  })
  .then(function(myJson) {
    console.log(myJson);
  });

post请求

fetch(url, {
  method: 'POST',
  body: JSON.stringify(data),
  headers: new Headers({
    'Content-Type': 'application/json'
  })
})

上传文件&提交表单

var formData = new FormData();
var fileField = document.querySelector("input[type='file']");

formData.append('username', 'abc123');
formData.append('avatar', fileField.files[0]);

fetch('https://example.com/upload', {
  method: 'PUT',
  body: formData
})
.then(response => response.json())
.catch(error => console.error('Error:', error))
.then(response => console.log('Success:', response));

移动端禁用和启用滚动、拖动

弹窗和弹框是网站应用中经常用到的技术,但是在页面弹出一个全屏的弹窗的时候,还是可以使用触摸来滑动弹窗下面的页面。

为了用户有更好的体验,可以使用以下代码,在弹窗的时候禁用滚动,弹窗关闭的时候再次启用滚动。

// left: 37, up: 38, right: 39, down: 40,
// spacebar: 32, pageup: 33, pagedown: 34, end: 35, home: 36
var keys = {37: 1, 38: 1, 39: 1, 40: 1};

function preventDefault(e) {
    e = e || window.event;
    if (e.preventDefault)
        e.preventDefault();
    e.returnValue = false;
}

function preventDefaultForScrollKeys(e) {
    if (keys[e.keyCode]) {
        preventDefault(e);
        return false;
    }
}

function disableScroll() {
    if (window.addEventListener) // older FF
        window.addEventListener('DOMMouseScroll', preventDefault, false);
    window.onwheel = preventDefault; // modern standard
    window.onmousewheel = document.onmousewheel = preventDefault; // older browsers, IE
    window.ontouchmove  = preventDefault; // mobile
    document.onkeydown  = preventDefaultForScrollKeys;
}

function enableScroll() {
    if (window.removeEventListener)
        window.removeEventListener('DOMMouseScroll', preventDefault, false);
    window.onmousewheel = document.onmousewheel = null;
    window.onwheel = null;
    window.ontouchmove = null;
    document.onkeydown = null;
}