Web 生成图片 html2canvas 示例

https://blog.liuguofeng.com/wp-content/uploads/2018/09/html2canvas.html

<!DOCTYPE html>
<html>
<head lang="en">
    <meta charset="UTF-8">
    <title>Using an existing canvas to draw on</title>
    <style>	
        button {
            clear: both;
            display: block;
        }
    </style>
</head>
<body>
<div><h1>这是HTML</h1>
	
    <div id="content" style="width:200px;height:200px;background: rgba(100, 255, 255, 0.5);">
	<img src='timg.jpg' style="width:100px;height:60px;">
	<span>这是文字</span>
	</div>
</div>
<h1>生成的图片</h1>
<canvas width="200" height="200"></canvas>
<script type="text/javascript" src="html2canvas.js"></script>
<button>点击生成图片</button>
<script type="text/javascript">
    var canvas = document.querySelector("canvas");
    document.querySelector("button").addEventListener("click", function() {
        html2canvas(document.querySelector("#content"), {canvas: canvas}).then(function(canvas) {
            console.log('Drew on the existing canvas');
        });
    }, false);
</script>
</body>
</html>

微信小程序 日历

JS

const app = getApp()
const util = require('../../utils/util.js')
const date = require('../../utils/date.js')

Page({
  data: {
    year: 0,
    month: 0,
    date: ['日', '一', '二', '三', '四', '五', '六'],
    dateArr: [],
    isToday: 0,
    isTodayWeek: false,
    todayIndex: 0,
    isClick:null
  },
  onLoad: function () {
    var now = new Date()
    console.log(now)
    if (now.length>0){
      var year = now.getFullYear();
      var month = now.getMonth() + 1;
      console.log('有 now')
    } else {
      var thedate = date.theDate()
      var year = date.getFullYear(thedate)
      var month = date.getMonth(thedate)
      console.log('没有 now')
    }
    this.start(year, month)
    this.isToday()
    console.log('onLoad done')

  },
  isToday:function(){
    let now = new Date();
    var theDay = '' + now.getFullYear() + util.formatNumber(now.getMonth() + 1) + util.formatNumber(now.getDate())
    this.setData({
      isToday: theDay,
      isClick: theDay
    })
  },
  start: function (year, month){
    month = Number(month)
    this.dateInit(year, month-1);
    this.setData({
      year: year,
      month: month,
    })
  },
  dateInit: function (setYear, setMonth) {
    //全部时间的月份都是按0~11基准,显示月份才+1
    let dateArr = [];						//需要遍历的日历数组数据
    let arrLen = 0;							//dateArr的数组长度
    let now = setYear ? new Date(setYear, setMonth) : new Date();
    let year = setYear || now.getFullYear();
    let nextYear = 0;
    let month = setMonth || now.getMonth();					//没有+1方便后面计算当月总天数
    let nextMonth = (month + 1) > 11 ? 1 : (month + 1);
    let startWeek = new Date(year , (month + 1) ,1).getDay();							//目标月1号对应的星期
    let dayNums = new Date(year, nextMonth, 0).getDate();				//获取目标月有多少天
    let obj = {};
    let num = 0;

    if (month + 1 > 11) {
      nextYear = year + 1;
      dayNums = new Date(nextYear, nextMonth, 0).getDate();
    }
    arrLen = startWeek + dayNums;
    console.log('startWeek')
    console.log(startWeek)
    for (let i = 0; i < arrLen; i++) {
      if (i >= startWeek) {
        num = i - startWeek + 1;
        obj = {
          isToday: '' + year + util.formatNumber(month + 1) + util.formatNumber(num),
          dateNum: num,
          weight: 6
        }
      } else {
        obj = {};
      }
      console.log(obj)
      dateArr[i] = obj;
    }
    console.log(dateArr)
    this.setData({
      dateArr: dateArr
    })

    let nowDate = new Date();
    let nowYear = nowDate.getFullYear();
    let nowMonth = nowDate.getMonth() + 1;
    let nowWeek = nowDate.getDay();
    let getYear = setYear || nowYear;
    let getMonth = setMonth >= 0 ? (setMonth + 1) : nowMonth;

    if (nowYear == getYear && nowMonth == getMonth) {
      this.setData({
        isTodayWeek: true,
        todayIndex: nowWeek
      })
    } else {
      this.setData({
        isTodayWeek: false,
        todayIndex: -1
      })
    }
  },
  lastMonth: function () {
    //全部时间的月份都是按0~11基准,显示月份才+1
    let year = this.data.month - 2 < 0 ? this.data.year - 1 : this.data.year;
    let month = this.data.month - 2 < 0 ? 11 : this.data.month - 2;
    this.start(year, Number(month) + 1)
  },
  nextMonth: function () {
    //全部时间的月份都是按0~11基准,显示月份才+1
    let year = this.data.month > 11 ? this.data.year + 1 : this.data.year;
    let month = this.data.month > 11 ? 0 : this.data.month;
    this.start(year, Number(month) + 1)
  },
  dateClick:function(res){
    console.log(res)
    var clickDay = res.currentTarget.dataset.date
    console.log(clickDay)
    this.setData({
      isClick:clickDay
    })
  }
})

WXML

<view class='wrap'>
  <view>
    <view class='date-show'>
      <view class='lt-arrow' bindtap='lastMonth'>
        <image src='/resource/cal/next.png' mode='aspectFit'></image>
      </view>
      {{year}}年{{month}}月
      <view class='rt-arrow' bindtap='nextMonth'>
        <image src='/resource/cal/next.png' mode='aspectFit'></image>
      </view>
    </view>
  </view>
  <view class='header'>
    <view wx:for='{{date}}' wx:key='{{key}}' class='{{(index == todayIndex) && isTodayWeek ? "weekMark" : ""}}'>{{item}}
      <view></view>
    </view>
  </view>
  <view class='date-box'>
    <view wx:for='{{dateArr}}' wx:key='{{key}}' class='{{isToday == item.isToday ? "nowDay" : ""}} {{isClick!=null && isClick == item.isToday ? "clickDay":""}}' data-date='{{item.isToday}}' bindtap='dateClick'>
      <view class='date-head'>
        <view>{{item.dateNum}}</view>
      </view>
      <view class='date-weight'>
        <block wx:if="{{item.weight}}">
          <view class='checkTwo'></view>
        </block>
      </view>
    </view>
  </view>
</view>

WXSS

.date-show{
	position: relative;
	width: 250rpx;
	font-family: PingFang-SC-Regular;
	font-size: 40rpx;
	color: #282828;
	text-align: center;
	margin: 59rpx auto 10rpx;
}
.lt-arrow,.rt-arrow{
	position: absolute;
	top: 1rpx;
	width: 60rpx;
	height: 60rpx;
}
.lt-arrow image,.rt-arrow image{
	width: 26rpx;
	height: 26rpx;
}
.lt-arrow{
	left: -110rpx;
	transform: rotate(180deg);
}
.rt-arrow{
	right: -100rpx;
}
.header{
	font-size: 0;
	padding: 0 24rpx;
}
.header>view{
	display: inline-block;
	width: 14.285%;
	color: #333;
	font-size: 30rpx;
	text-align: center;
	border-bottom: 1px solid #D0D0D0;
	padding: 10rpx 0;
}
.weekMark{
	position: relative;
}
.weekMark view{
	position: absolute;
	bottom: 0;
	left: 0;
	width: 100%;
	border-bottom: 1px solid #22A7F6;
}
.date-box{
	font-size: 0;
	padding: 10rpx 0;
}
.date-box>view{
	position: relative;
	display: inline-block;
	width: 14.285%;
	color: #020202;
	font-size: 40rpx;
	text-align: center;
	vertical-align: middle;
	margin: 10rpx 0 0;
}
.date-head{
	height: 60rpx;
	line-height: 60rpx;
	font-size: 26rpx;
}
.nowDay .date-head{
	width: 60rpx;
	border-radius: 50%;
	text-align: center;
	color: #fff;
	background-color: #22A7F6;
  opacity:0.5;
	margin: 0 auto;
}

.clickDay .date-head{
	width: 60rpx;
	border-radius: 50%;
	text-align: center;
	color: #fff;
	background-color: #22A7F6;
  opacity:1;
	margin: 0 auto;
}
.date-weight{
	font-size: 22rpx;
	padding: 15rpx 0 0;
  display: flex;
  flex-direction: row;
  justify-content: center;
  align-items: center;
}
.nowDay .date-weight{
	color: #22A7F6;
}
.clickDay .date-weight{
	color: #22A7F6;
}
.one{
	position: absolute;
	bottom: 0;
	right: 5rpx;
	width: 20rpx;
	height: 20rpx;
	border-radius: 50%;
	background-color: red;
}
.two{
	position: absolute;
	bottom: 30rpx;
	right: 5rpx;
	width: 20rpx;
	height: 20rpx;
	border-radius: 50%;
	background-color: blue;
}
.checkTwo{
  width:10rpx;
  height:10rpx;
  border-radius:50%;
  background-color: blue;
}

util.js

const formatTime = date => {
  const year = date.getFullYear()
  const month = date.getMonth() + 1
  const day = date.getDate()
  const hour = date.getHours()
  const minute = date.getMinutes()
  const second = date.getSeconds()

  return [year, month, day].map(formatNumber).join('/') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}

const formatNumber = n => {
  n = n.toString()
  return n[1] ? n : '0' + n
}

module.exports = {
  formatTime: formatTime,
  formatNumber: formatNumber
}

date.js

const app = getApp()
const util = require('util.js')
const dateAdd = function (startDate, days) {
  startDate = new Date(startDate);
  startDate = +startDate + days * 1000 * 60 * 60 * 24;
  startDate = new Date(startDate);
  var nextStartDate = startDate.getFullYear() + "-" + util.formatNumber((startDate.getMonth() + 1)) + "-" + util.formatNumber(startDate.getDate());
  return nextStartDate;
}

const theDate = function (num = 0){
  var thisDate = new Date();
  var myDate = new Date(thisDate.getTime() + 24 * 60 * 60 * 1000 * num)
  var theDate = myDate.getFullYear() + '-' + util.formatNumber((myDate.getMonth() + 1)) + '-' + util.formatNumber(myDate.getDate())
  return theDate
}

// 时间比较函数
const compareDate = function (startDate, endDate) {
  var arrStart = startDate.split("-");
  var startTime = new Date(arrStart[0], arrStart[1], arrStart[2]);
  var startTimes = startTime.getTime();
  var arrEnd = endDate.split("-");
  var endTime = new Date(arrEnd[0], arrEnd[1], arrEnd[2]);
  var endTimes = endTime.getTime();
  if (endTimes <= startTimes) {
    return false;
  }
  return true;
}

const getHour = function () {
  var myDate = new Date();
  var theHour = myDate.getHours()
  return theHour
}

const getDays = function (date1, date2) {
  var date1Str = date1.split("-");//将日期字符串分隔为数组,数组元素分别为年.月.日  
  //根据年 . 月 . 日的值创建Date对象  
  var date1Obj = new Date(date1Str[0], (date1Str[1] - 1), date1Str[2]);
  var date2Str = date2.split("-");
  var date2Obj = new Date(date2Str[0], (date2Str[1] - 1), date2Str[2]);
  var t1 = date1Obj.getTime();
  var t2 = date2Obj.getTime();
  var dateTime = 1000 * 60 * 60 * 24; //每一天的毫秒数  
  var minusDays = Math.floor(((t2 - t1) / dateTime));//计算出两个日期的天数差  
  var days = Math.abs(minusDays);//取绝对值  
  return days;
}  

const getWeek = function (theDate) {
  var weekDay = ["周天", "周一", "周二", "周三", "周四", "周五", "周六"];
  var weekName = weekDay[new Date(Date.parse(theDate)).getDay()]
  return weekName
}

const getDate = function (theDate) {
  var getDate = new Date(Date.parse(theDate)).getDate() 
  return util.formatNumber(getDate)
}

const getMonth = function (theDate) {
  var getMonth = new Date(Date.parse(theDate)).getMonth() + 1
  return util.formatNumber(getMonth)
}

const getFullYear = function (theDate) {
  var getFullYear = new Date(Date.parse(theDate)).getFullYear()
  return getFullYear
}

const diffDate = function (startDate, endDate) {
  console.log('调用了 diffDate 函数' + startDate + endDate)
  var startNewDate = new Date(startDate)
  var endNewDate = new Date(endDate)
  var startTime = startNewDate.getTime()
  var endTime = endNewDate.getTime()
  var diff = endTime - startTime
  var days = diff / (24 * 60 * 60)
  console.log(parseInt(days))
  return parseInt(days)
}

module.exports = {
  dateAdd: dateAdd,
  theDate: theDate,
  compareDate: compareDate,
  getHour: getHour,
  getWeek: getWeek,
  getDate: getDate,
  getMonth: getMonth,
  getFullYear: getFullYear,
  getDays:getDays,
  diffDate: diffDate
}

微信小程序 利用 swiper 制作 tab 选项卡

WXML

<view class="swiper-tab">
    <view class="swiper-tab-item {{currentTab==0?'active':''}}" data-current="0" bindtap="clickTab">一</view>
    <view class="swiper-tab-item {{currentTab==1?'active':''}}" data-current="1" bindtap="clickTab">二</view>
    <view class="swiper-tab-item {{currentTab==2?'active':''}}" data-current="2" bindtap="clickTab">三</view>
</view>

<swiper current="{{currentTab}}" duration="300"  bindchange="swiperTab">
    <swiper-item><view>这是第一屏内容</view></swiper-item>
    <swiper-item><view>这是第二屏内容</view></swiper-item>
    <swiper-item><view>这是第三屏内容</view></swiper-item>
</swiper>

JS

var app = getApp()
Page({
  data: {
    currentTab: 0
  },
  onLoad: function (options) {
    // 页面初始化 options为页面跳转所带来的参数
  },
  //滑动切换
  swiperTab: function (e) {
    var that = this;
    that.setData({
      currentTba: e.detail.current
    });
  },
  //点击切换
  clickTab: function (e) {
    var that = this;
    if (this.data.currentTab === e.target.dataset.current) {
      return false;
    } else {
      that.setData({
        currentTab: e.target.dataset.current
      })
    }
  }
})

WXSS

.swiper-tab{
    width: 100%;
    border-bottom: 2rpx solid #ccc;
    text-align: center;
    height: 88rpx;
    line-height: 88rpx;
    font-weight: bold;
}
.swiper-tab-item{
    display: inline-block;
    width: 33.33%;
    color:red;
}
.active{
    color:aqua;
    border-bottom: 4rpx solid red;
}

升级 Swoole

pecl upgrade swoole

或重新编译安装 

https://blog.liuguofeng.com/php-source-code-compilation-and-installation-swoole-extension/

查看 Swoole 版本

[root@VM_0_2_centos ~]# php --ri swoole
PHP Warning:  Module 'swoole' already loaded in Unknown on line 0

swoole

swoole support => enabled
Version => 4.1.1
Author => Swoole Group[email: team@swoole.com]
coroutine => enabled
epoll => enabled
eventfd => enabled
signalfd => enabled
cpu affinity => enabled
spinlock => enabled
rwlock => enabled
pcre => enabled
zlib => enabled
mutex_timedlock => enabled
pthread_barrier => enabled
futex => enabled

Directive => Local Value => Master Value
swoole.enable_coroutine => On => On
swoole.aio_thread_num => 2 => 2
swoole.display_errors => On => On
swoole.use_namespace => On => On
swoole.use_shortname => On => On
swoole.fast_serialize => Off => Off
swoole.unixsock_buffer_size => 8388608 => 8388608

出现错误提示

PHP Warning:  Module 'swoole' already loaded in Unknown on line 0

打开 php.ini

vim /usr/local/php/etc/php.ini

查找 swoole,插件被载入了两次,删除一个即可,删除重启

Swoole 与 纯 PHP ab 压力测试

服务器配置

  • 操作系统CentOS 7.2 64位
  • CPU1 核
  • 内存2 GB
  • 公网带宽1 Mbps

Swoole  2.1.2-alpha

<?php
$http = new swoole_http_server("127.0.0.1", 9501);

$http->on("start", function ($server) {
    echo "Swoole http server is started at http://127.0.0.1:9501\n";
});

$http->on("request", function ($request, $response) {
    $response->header("Content-Type", "text/plain");
    $response->end("Hello World\n");
});

$http->start();
ab -c100 -n100000 http://127.0.0.1:9501/
Server Software:        nginx
Server Hostname:        127.0.0.1
Server Port:            80

Document Path:          /helloworld.php
Document Length:        11 bytes

Concurrency Level:      100
Time taken for tests:   20.609 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      18900000 bytes
HTML transferred:       1100000 bytes
Requests per second:    4852.13 [#/sec] (mean)
Time per request:       20.609 [ms] (mean)
Time per request:       0.206 [ms] (mean, across all concurrent requests)
Transfer rate:          895.56 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.1      0       3
Processing:     3   20   6.3     20     153
Waiting:        1   20   6.3     20     153
Total:          3   21   6.3     20     154

PHP 7.2.3

<?php
echo 'hello world';
ab -c100 -n100000 http://127.0.0.1/helloworld.php
Server Software:        swoole-http-server
Server Hostname:        127.0.0.1
Server Port:            9501

Document Path:          /
Document Length:        12 bytes

Concurrency Level:      100
Time taken for tests:   5.758 seconds
Complete requests:      100000
Failed requests:        0
Write errors:           0
Total transferred:      16100000 bytes
HTML transferred:       1200000 bytes
Requests per second:    17368.14 [#/sec] (mean)
Time per request:       5.758 [ms] (mean)
Time per request:       0.058 [ms] (mean, across all concurrent requests)
Transfer rate:          2730.73 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    1   0.4      1       4
Processing:     3    4   0.9      4      54
Waiting:        2    3   0.6      3      52
Total:          4    6   0.9      5      54

对 Laravel 5.5 和 ThinkPHP 5.0.20 进行了 ab 压力测试

是不是我配置有问题?

Laravel 5.5 输出 echo 'hello world'

Server Software:        nginx/1.11.5
Server Hostname:        laravel55.a.cc
Server Port:            80

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   36.029 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      101442 bytes
HTML transferred:       1100 bytes
Requests per second:    2.78 [#/sec] (mean)
Time per request:       3602.922 [ms] (mean)
Time per request:       360.292 [ms] (mean, across all concurrent requests)
Transfer rate:          2.75 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.4      0       1
Processing:   348 3458 724.6   3592    4386
Waiting:      348 3457 724.6   3592    4386
Total:        349 3458 724.6   3592    4386

ThinkPHP 5.0.20 输出 echo 'hello world'

Server Software:        nginx/1.11.5
Server Hostname:        thinkphp50.a.cc
Server Port:            80

Document Path:          /
Document Length:        11 bytes

Concurrency Level:      10
Time taken for tests:   4.198 seconds
Complete requests:      100
Failed requests:        9
   (Connect: 0, Receive: 0, Length: 9, Exceptions: 0)
Non-2xx responses:      9
Total transferred:      20852 bytes
HTML transferred:       2558 bytes
Requests per second:    23.82 [#/sec] (mean)
Time per request:       419.839 [ms] (mean)
Time per request:       41.984 [ms] (mean, across all concurrent requests)
Transfer rate:          4.85 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.5      0       1
Processing:    64  401 123.5    401     604
Waiting:       64  401 123.6    401     604
Total:         64  402 123.5    402     604

电脑配置

使用 Apache 的 ab 工具进行 PHP WordPress 的压力测试

使用 Apache 的 ab 工具测试了下几个网站的并发

本机配置

本地测试 echo 'hello world'

./ab -c10 -n1000 http://localhost/helloworld.php
Server Software:        nginx/1.11.5
Server Hostname:        wp.a.cc
Server Port:            80

Document Path:          /helloworld.php
Document Length:        25 bytes

Concurrency Level:      10
Time taken for tests:   1.041 seconds
Complete requests:      1000
Failed requests:        12
   (Connect: 0, Receive: 0, Length: 12, Exceptions: 0)
Non-2xx responses:      1000
Total transferred:      219284 bytes
HTML transferred:       26776 bytes
Requests per second:    960.29 [#/sec] (mean)
Time per request:       10.414 [ms] (mean)
Time per request:       1.041 [ms] (mean, across all concurrent requests)
Transfer rate:          205.64 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.4      0       1
Processing:     2   10   4.7      9      22
Waiting:        2   10   4.6      8      22
Total:          3   10   4.7      9      22

测试 phpinfo()

./ab -c10 -n1000 http://localhost/phpinfo.php
Server Software:        nginx/1.11.5
Server Hostname:        wp.a.cc
Server Port:            80

Document Path:          /phpinfo.php
Document Length:        98215 bytes

Concurrency Level:      10
Time taken for tests:   4.408 seconds
Complete requests:      1000
Failed requests:        107
   (Connect: 0, Receive: 0, Length: 107, Exceptions: 0)
Non-2xx responses:      17
Total transferred:      96733608 bytes
HTML transferred:       96548186 bytes
Requests per second:    226.85 [#/sec] (mean)
Time per request:       44.083 [ms] (mean)
Time per request:       4.408 [ms] (mean, across all concurrent requests)
Transfer rate:          21429.39 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.4      0       1
Processing:    10   44   8.9     43      78
Waiting:        9   43   8.9     42      78
Total:         10   44   8.9     43      78

测试新装 WordPress

./ab -c10 -n100 http://localhost/wordpress/
Server Software:        nginx/1.11.5
Server Hostname:        wp.a.cc
Server Port:            80

Document Path:          /wordpress/
Document Length:        52976 bytes

Concurrency Level:      10
Time taken for tests:   45.738 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      5323100 bytes
HTML transferred:       5297600 bytes
Requests per second:    2.19 [#/sec] (mean)
Time per request:       4573.759 [ms] (mean)
Time per request:       457.376 [ms] (mean, across all concurrent requests)
Transfer rate:          113.66 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.5      0       1
Processing:   447 4366 790.2   4507    5239
Waiting:      446 4366 790.2   4507    5239
Total:        447 4367 790.2   4507    5240

WordPress 安装 WP Super Cache 缓存插件

./ab -c10 -n100 http://localhost/wordpress/
Server Software:        nginx/1.11.5
Server Hostname:        wp.a.cc
Server Port:            80

Document Path:          /wordpress/
Document Length:        53120 bytes

Concurrency Level:      10
Time taken for tests:   2.721 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      5338000 bytes
HTML transferred:       5312000 bytes
Requests per second:    36.75 [#/sec] (mean)
Time per request:       272.131 [ms] (mean)
Time per request:       27.213 [ms] (mean, across all concurrent requests)
Transfer rate:          1915.58 [Kbytes/sec] received

Connection Times (ms)
              min  mean[+/-sd] median   max
Connect:        0    0   0.4      0       1
Processing:    32  260  47.2    268     328
Waiting:       32  260  47.2    268     328
Total:         32  261  47.2    269     328