[转载]百度地图API详解之地图容器

mikel阅读(1272)

[转载]百度地图API详解之地图容器 – jz1108 – 博客园.

百度地图API详解

地图容器就是包含地图区域的那个框框,这个有什么可说的呢?请您往下看。

通常我们会给地图容器一个固定的尺寸:


假设上面的div元素就是我们的地图容器,那么地图显示范围就是500×320:

有时我们希望地图容器随着浏览器变化而变化,那么我们将样式调整为:

html{height:100%}
body{height:100%;margin:0px;padding:0px}
#map{height:100%}

这时地图区域会跟随着浏览器的大小自动变化,另外API也会根据外框尺寸的变化自 动铺设图块以保证视野内地图是完整的。通过监听map的resize事件可得知地图容器在什么时候发生变化了。注意在容器大小发生变化时,API默认保证 地图到左边和上边的距离始终一致,这样做的目的也是保证地图相对于人眼的位置不发生变化(因为用户在修改浏览器窗口尺寸时通常都会调整右下方的点),简单 说就是用户不会感到地图有任何的“跳动”:

我们看到随着浏览器窗口变大,地图逐渐展示出北京市东南区域的图块,而整个地图相对于左上角的位置没有发生变化。还有一点需要注意,这种情况下地图中心点是发生变化的了,之前中心位于天安门附近,之后中心点向东南方向移动了。我们可以通过监听resize事件来验证:

map.addEventListener('resize', function(){
console.log(this.getCenter().lng + ',' + this.getCenter().lat);
});

下面是在firebug中看到的数据,可以看出经度不断变大,纬度不断减小,说明中心点向东南方向移动了。

如果希望不管容器怎样变化地图中心点都保持一致该怎么办呢?首先你需要禁止地图自动适应容器变化,这可通过设置MapOptions的enableAutoResize属性为false或者调用Map的disableAutoResize方法来实现:

var map = new Map('map', {enableAutoResize: false});

此时你会发现容器变化时,地图不再自动铺图,也不会派发resize事件了。此时我们需要自己监听body的resize事件:


resizeMap方法中需要做如下工作:

function resizeMap() {
var center = map.getCenter();
map.checkResize();
map.setCenter(center);
}

我们先获取地图中心点,然后调用Map的checkResize方法通知地图容器发生变 化,此时地图会进行铺图,最后我们设置地图中心点为之前的获取的值。这样不论地图容器如何变化,地图中心点始终保持一致。去哪(qunar.com)酒店 的页面上就有这样一个地图,其提供了大图模式和小图模式切换的按钮,切换后地图中心点保持一致(地址在此)。

还有一种稍复杂的情况,对于以地图应用为主的网站,通常界面布局会是如下样式:

网站上方是搜索区域,左侧是结果展示区域,剩下的区域用来展示地图。网站很可能提 供一个全屏按钮来隐藏上面和左面的区域,为了获得更好的用户体验,我们希望此时地图相对于整个浏览器的位置不会发生变化,仿佛上面和左面的区域是盖在地图 上的。这样空说可能不好理解,我们来看实际的例子:

首先准备完整的HTML页面:


百度地图API详解
<!-- html{height:100%;} body{height:100%;margin:0px;padding:0px;} #top{width:100%;height:79px;border-bottom:1px solid #ccc;background:#eee;} #aside{position:absolute;top:80px;width:159px;border-right:1px solid #bbb;background:#ddd;} #map{margin-left:160px;} -->
<script src="http://api.map.baidu.com/api?v=1.2" type="text/javascript"></script> <script type="text/javascript">// <!&#91;CDATA&#91;
  function checkSize() { var h = document.documentElement.clientHeight; document.getElementById('aside').style.height = h - 80 + "px"; document.getElementById('map').style.height = h - 80 + "px"; } function init() { checkSize(); var map = new BMap.Map('map'); map.centerAndZoom('北京'); window.map = map; }
// &#93;&#93;></script>

页面效果如下:

这时我们修改浏览器窗口大小,地图会自动铺图并且地图相对于左边和上边的距离保持不变,一切效果OK。下面我们添加编写一个全屏函数:

function toFullScreen() {
document.getElementById('top').style.display = 'none';
document.getElementById('aside').style.display = 'none';
var h = document.documentElement.clientHeight;
var mapContainer = document.getElementById('map');
mapContainer.style.height = h + "px";
mapContainer.style.marginLeft = "0";
}

这个函数将两个区域进行隐藏,同时调整地图容器,我们在控制台调用这个方法,会得到这个结果:

和之前的图对比可以发现地图区域向左上方移动了,这样会给用户地图“跳动”的感觉,为了让地图与用户眼睛的位置保持一致,我们需要修改toFullScreen函数:

function toFullScreen() {
map.disableAutoResize();
var h = document.documentElement.clientHeight;
var curPix = map.pointToPixel(map.getCenter());
var newPix = new BMap.Pixel(curPix.x - 80, curPix.y - 40);
var newCenter = map.pixelToPoint(newPix);
document.getElementById('top').style.display = 'none';
document.getElementById('aside').style.display = 'none';
var mapContainer = document.getElementById('map');
mapContainer.style.height = h + "px";
mapContainer.style.marginLeft = "0";
map.checkResize();
map.setCenter(newCenter);
map.enableAutoResize();
}

这里大致的思路是,首先停止地图自动适应容器变化,接着通过坐标转换得到当前中心 点对应的像素坐标curPix,再计算出全屏后中心点的像素坐标newPix,进而转换为经纬度newCenter。下面调用修改容器尺寸并调用 checkResize通知地图容器发生变化,接着再重新设置中心点并恢复自动适应容器变化。效果如下:

此时,全屏过程中地图没有任何的“跳动”,从而提供了较好的用户体验。谷歌地图也使用了类似的效果,当左侧面板收起时,地图区域自动向左侧扩展,而没有向左侧跳动。

最后说明一下,上面的checkResize方法没有考虑全屏幕的情况,因为它不是本文的重点,就不再这里给出具体代码了。

[转载]SQL点滴19—T-SQL中的透视和逆透视

mikel阅读(1084)

[转载]SQL点滴19—T-SQL中的透视和逆透视 – nd – 博客园.

透视

今天抽一点时间来看看透视和逆透视语句,简单的说就是行列转换。假设一个销售表中存放着产品号,产品折扣,产品价格三个列,每一种产品号可能有多种折扣,每一种折扣只对应一个产品价格。下面贴出建表语句和插入数据语句。

create table SalesOrderDetail(
ProductID int unique,
UnitPriceDiscount float,
ProductPrice float
)
insert into SalesOrderDetail values
(711,.00,12),
(711,.00,13),
(711,.02,17),
(711,.02,16),
(711,.05,19),
(711,.05,20),
(711,.10,21),
(711,.10,22),
(711,.15,23),
(711,.15,24),
(747,.00,41),
(747,.00,42),
(747,.02,45),
(747,.02,46),
(776,.20,50),
(776,.20,49),
(776,.35,52),
(776,.35,53)

首先来看一条查询语句

select ProductID,UnitPriceDiscount,SUM(ProductPrice) as SumPrice
from SalesOrderDetail
group by ProductID,UnitPriceDiscount
order by ProductID,UnitPriceDiscount

这条语句查询每一种产品针对每一种折扣的价钱总和,查询结果如下图1

图1

从图中我们可以看出771号产品有4种折扣,747号产品有2种折扣,776号产品有2种折扣。现在如果我们想知道每一种产品折扣,每一种产品的销售总价是多少,如下图2

图2

如图对于折扣0,产品711的总价是25,对以折扣0.02,产品711的总价是33等等不再列举。原来的行是产品号,现在产品号变成了列,原来的折扣变成了现在的第一列。这就是数据透视的效果。下面我们开看看是这个效果是如何用语句实现的。

select * from
(select sod.ProductPrice,sod.ProductID,sod.UnitPriceDiscount from SalesOrderDetail sod) so
pivot
(
sum(so.ProductPrice) for so.ProductID in([711],[747],[776])
) as pt
order by UnitPriceDiscount

首选创建子查询(select sod.ProductPrice,sod.ProductID,sod.UnitPriceDiscount from SalesOrderDetail sod) so ,透视运算符要使用这个子查询中的数据进行聚合运算,此外输出显示也要用到子查询中的列。代码生成一个别名为so的表值表达式。在这个表中使用pivot 在特定的列上进行聚合,这里是对so.ProductPrice进行聚合,聚合针对so.ProductID进行。在这个例子中对三种产品的中的每一种创 建一个列。这个相当于group by,从so表达式中进行数据筛选。不过这里没有选出ProductPrice,仅仅生成每行三个列,每一种产品为一个列的结果集。因此带有povit的 表值表达式生成一个临时的结果集,将这个结果集命名为pt,使用这个结果集生成我们需要的输出。如果想要得到一个更加合适的列名可以修改筛选条件。如下:

select pt.UnitPriceDiscount,[711] as Product711,[747] as Product747,[776] as Product747 from
(select sod.ProductPrice,sod.ProductID,sod.UnitPriceDiscount from SalesOrderDetail sod) so
pivot
(
sum(so.ProductPrice) for so.ProductID in([711],[747],[776])
) as pt
order by UnitPriceDiscount

输出的结果如下图3

图3

逆透视

这次我们首先看语句和查询结果再分析,语句如下:


select ProductID,UnitPriceDiscount,ProductPrice
from
(select UnitPriceDiscount,Product711,Product747,Product776 from #Temp1) as up1
unpivot(ProductPrice for ProductID in(Product711,Product747,Product776)) as up2
order by ProductID

查询结果如下图4:



图4



首先我们来看看逆透视得到了一个什么样的结果。对于每一种产品的每一种折扣查询得 到他们的合计售价,这个和上面图1中的结果是一样的,是的,它和透视之前的结果是相同的。逆透视和透视并不是完全相反。Pivot会执行聚合,把可能存在 的多个行合并输出得到一行。由于已经进行了合并,unpivot无法重新生成原始的表值表达式,unpivot输入中的null值将在输出中消失,尽管在 pivot操作之前输入中可能存在原始的null值。如图5是他们的比较。在图中我们可以看到NULL值下面一个图中没有NULL值,刚好有9行。下图把 他们放在一起比较。



图5

下 面我们来剖析一下上面的语句到底做了些什么。首先是一个表值函数(select UnitPriceDiscount,Product711,Product747,Product776 from #Temp1) as up1,这个表值函数从透视结果,也就是临时表中,然后针对每一个产品号进行逆透视:unpivot(ProductPrice for ProductID in(Product711,Product747,Product776)) as up2,然后从逆透视结果中选择ProductID ,ProductPrice,从表值函数中选择UnitPriceDiscount。

延伸阅读

一个例子还不足以让我们理解这个语句,下面来看看TechNet中的例子。

SELECT DaysToManufacture, AVG(StandardCost) AS AverageCost FROM Production.Product

GROUP BY DaysToManufacture;

这个语句查出Product表中的制造时间和平均成本,得到如下的结果

图6

如图可以看到没有制造时间为3天的产品,这里留下一个伏笔,在透视之后会出现一个NULL值。下面使用透视语句对它进行行列转换,就是使用0,1,2,3来作为列,使用具体的制造成本作为行数据。语句如下

select
'AverageCost' as Cost_Sorted_By_Production_Days,
[0],[1],[3],[4]
from
(select DaysToManufacture,StandardCost from Production.Product) as SourceTable
pivot
(avg(StandardCost) for DaysToManufacture in ([0],[1],[3],[4])) as PivotTable

依旧,首先用一个表值表达式把要透视的列和透视的项选择出来,然后使用透视语句针对每一个项计算平均成本,最后从这个透视结果中选择出结果。
结果如下图7,我们可以看到制造时间为3天的产品没有一个对应的平均成本。

图7

下面这个例子稍微复杂一点。

SELECT VendorID,count(PurchaseOrderID) as PurchaseCunt
FROM Purchasing.PurchaseOrderHeader group by VendorID

这条语句查询得到每个供应商和他对应的交易号的个数,也就是每个供应商成交的交易次数。如图8列举出部分结果

图8

从图中我们可以看到供应商1共成交51比交易,供应商2共成交51笔交易。如果我们想查出这些交易分别是和那些雇员成交的应该怎么写呢?首先我们来看看表中全部的雇员情况。

select distinct(EmployeeID) from Purchasing.PurchaSEOrderHeader

查询结果如图9

图9

如上图我们可以看到共有12个雇员有成交记录。对于这些雇员,如下查询语句

SELECT
VendorID,
[164] AS Emp164,
[198] AS Emp198,
[223] AS Emp223,
[231] AS Emp231,
[233] AS Emp233,
[238] as Emp238,
[241] as Emp241,
[244] as Emp244,
[261] as Emp261,
[264] as Emp264,
[266] as Emp266,
[274] as Emp274
FROM
(SELECT PurchaseOrderID,EmployeeID,VendorID
FROM Purchasing.PurchaseOrderHeader) p
PIVOT
(
COUNT (PurchaseOrderID)
FOR EmployeeID IN
( [164], [198], [223], [231],[233],[238],[241],[244],[261],[264],[266],[274])
) AS pvt
ORDER BY pvt.VendorID;

查询结果如下图10

图10

可以 简单地计算一下1+4+3+5+4+4+4+5+5+4+5+6+2刚好等于51,分开来看就是1号供应商分别和164号雇员成交4比记录,和198号雇员成交3比记录等等。

[转载]基于prototype的输入自动提示autocomplete

mikel阅读(959)

[转载]基于prototype的输入自动提示autocomplete – ╰⑥月の雨╮ – 博客园.

对老外的加了修改,对中文的支持和条数的显示

原作者地址: http://www.devbridge.com/projects/autocomplete/

效果:

var Autocomplete = function(el, options){
  this.el = $(el);
  this.id = this.el.identify();
  this.el.setAttribute('autocomplete','off');
  this.suggestions = [];
  this.data = [];
  this.badQueries = [];
  this.selectedIndex = -1;
  this.currentValue = this.el.value;
  this.intervalId = 0;
  this.cachedResponse = [];
  this.instanceId = null;
  this.onChangeInterval = null;
  this.ignoreValueChange = false;
  this.serviceUrl = options.serviceUrl;
  this.numbers = [];//条数
  this.options = {
    autoSubmit:false,
    minChars:1,
    maxHeight:300,
    deferRequestBy:0,
    width:0,
    showNumber:true, //是否显示条数
    container:null
  };
  if(options){ Object.extend(this.options, options); }
  if(Autocomplete.isDomLoaded){
    this.initialize();
  }else{
    Event.observe(document, 'dom:loaded', this.initialize.bind(this), false);
  }
};

Autocomplete.instances = [];
Autocomplete.isDomLoaded = false;

Autocomplete.getInstance = function(id){
  var instances = Autocomplete.instances;
  var i = instances.length;
  while(i--){ if(instances[i].id === id){ return instances[i]; }}
};

Autocomplete.highlight = function(value, re){
  return value.replace(re, function(match){ return '<strong>' + match + '<\/strong>' });
};

Autocomplete.prototype = {

  killerFn: null,

  initialize: function() {
    var me = this;
    this.killerFn = function(e) {
      if (!$(Event.element(e)).up('.autocomplete')) {
        me.killSuggestions();
        me.disableKillerFn();
      }
    } .bindAsEventListener(this);

    if (!this.options.width) { this.options.width = this.el.getWidth(); }

    var div = new Element('div', { style: 'position:absolute;' });
    div.update('<div class="autocomplete-w1"><div class="autocomplete-w2"><div class="autocomplete" id="Autocomplete_' + this.id + '" style="display:none; width:' + this.options.width + 'px;"></div></div></div>');

    this.options.container = $(this.options.container);
    if (this.options.container) {
      this.options.container.appendChild(div);
      this.fixPosition = function() { };
    } else {
      document.body.appendChild(div);
    }

    this.mainContainerId = div.identify();
    this.container = $('Autocomplete_' + this.id);
    this.fixPosition();
    
    Event.observe(this.el, window.opera ? 'keypress':'keydown', this.onKeyPress.bind(this));
    Event.observe(this.el, 'keyup', this.onKeyUp.bind(this));
    Event.observe(this.el, 'blur', this.enableKillerFn.bind(this));
    Event.observe(this.el, 'focus', this.fixPosition.bind(this));
    this.container.setStyle({ maxHeight: this.options.maxHeight + 'px' });
    this.instanceId = Autocomplete.instances.push(this) - 1;
  },

  fixPosition: function() {
    var offset = this.el.cumulativeOffset();
    $(this.mainContainerId).setStyle({ top: (offset.top + this.el.getHeight()) + 'px', left: offset.left + 'px' });
  },

  enableKillerFn: function() {
    Event.observe(document.body, 'click', this.killerFn);
  },

  disableKillerFn: function() {
    Event.stopObserving(document.body, 'click', this.killerFn);
  },

  killSuggestions: function() {
    this.stopKillSuggestions();
    this.intervalId = window.setInterval(function() { this.hide(); this.stopKillSuggestions(); } .bind(this), 300);
  },

  stopKillSuggestions: function() {
    window.clearInterval(this.intervalId);
  },

  onKeyPress: function(e) {
    if (!this.enabled) { return; }
    switch (e.keyCode) {
      case Event.KEY_ESC:
        this.el.value = this.currentValue;
        this.hide();
        break;
      case Event.KEY_TAB:
      case Event.KEY_RETURN:
        if (this.selectedIndex === -1) {
          this.hide();
          return;
        }
        this.select(this.selectedIndex);
        if (e.keyCode === Event.KEY_TAB) { return; }
        break;
      case Event.KEY_UP:
        this.moveUp();
        break;
      case Event.KEY_DOWN:
        this.moveDown();
        break;
      default:
        return;
    }
    Event.stop(e);
  },

  onKeyUp: function(e) {
    switch (e.keyCode) {
      case Event.KEY_UP:
      case Event.KEY_DOWN:
        return;
    }
    clearInterval(this.onChangeInterval);
    if (this.currentValue !== this.el.value) {
      if (this.options.deferRequestBy > 0) {
        // Defer lookup in case when value changes very quickly:
        this.onChangeInterval = setInterval((function() {
          this.onValueChange();
        }).bind(this), this.options.deferRequestBy);
      } else {
        this.onValueChange();
      }
    }
  },

  onValueChange: function() {
    clearInterval(this.onChangeInterval);
    this.currentValue = this.el.value;
    this.selectedIndex = -1;
    if (this.ignoreValueChange) {
      this.ignoreValueChange = false;
      return;
    }
    if (this.currentValue === '' || this.currentValue.length < this.options.minChars) {
      this.hide();
    } else {
      this.getSuggestions();
    }
  },

  getSuggestions: function() {
    var cr = this.cachedResponse&#91;this.currentValue&#93;;
    if (cr && Object.isArray(cr.suggestions)) {
      this.suggestions = cr.suggestions;
      this.data = cr.data;
      this.numbers = cr.numbers;
      this.suggest();
    } else if (!this.isBadQuery(this.currentValue)) {
      new Ajax.Request(this.serviceUrl, {
        parameters: { query: this.currentValue },
        onComplete: this.processResponse.bind(this),
        method: 'get'
      });
    }
  },

  isBadQuery: function(q) {
    var i = this.badQueries.length;
    while (i--) {
      if (q.indexOf(this.badQueries&#91;i&#93;) === 0) { return true; }
    }
    return false;
  },

  hide: function() {
    this.enabled = false;
    this.selectedIndex = -1;
    this.container.hide();
  },

  suggest: function() {
    if (this.suggestions.length === 0) {
      this.hide();
      return;
    }
    var content = &#91;&#93;;
    var re = new RegExp('\\b' + this.currentValue.match(/&#91;\u4e00-\u9fa5a-zA-Z0-9&#93;+/g).join('|\\b'), 'gi');
    var numbersContent = '';
    this.suggestions.each(function(value, i) {
      if (Object.isArray(this.numbers) && this.options.showNumber){
        numbersContent = ' <span class="number">约' + this.numbers[i] + '条</span>';
      }
      content.push((this.selectedIndex === i ? '<div class="selected"' : '<div'), ' title="', value, '" onclick="Autocomplete.instances&#91;', this.instanceId, '&#93;.select(', i, ');" onmouseover="Autocomplete.instances&#91;', this.instanceId, '&#93;.activate(', i, ');">', Autocomplete.highlight(value, re), numbersContent, '</div>');
    } .bind(this));
    this.enabled = true;
    this.container.update(content.join('')).show();
  },

  processResponse: function(xhr) {
    var response;
    try {
      response = xhr.responseText.evalJSON();
      if (!Object.isArray(response.data)) { response.data = []; }
    } catch (err) { return; }
    this.cachedResponse[response.query] = response;
    if (response.suggestions.length === 0) { this.badQueries.push(response.query); }
    if (response.query === this.currentValue) {
      this.suggestions = response.suggestions;
      this.data = response.data;
      this.numbers = response.numbers;
      this.suggest(); 
    }
  },

  activate: function(index) {
    var divs = this.container.childNodes;
    var activeItem;
    // Clear previous selection:
    if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {
      divs[this.selectedIndex].className = '';
    }
    this.selectedIndex = index;
    if (this.selectedIndex !== -1 && divs.length > this.selectedIndex) {
      activeItem = divs[this.selectedIndex]
      activeItem.className = 'selected';
    }
    return activeItem;
  },

  deactivate: function(div, index) {
    div.className = '';
    if (this.selectedIndex === index) { this.selectedIndex = -1; }
  },

  select: function(i) {
    var selectedValue = this.suggestions[i];
    if (selectedValue) {
      this.el.value = selectedValue;
      if (this.options.autoSubmit && this.el.form) {
        this.el.form.submit();
      }
      this.ignoreValueChange = true;
      this.hide();
      this.onSelect(i);
    }
  },

  moveUp: function() {
    if (this.selectedIndex === -1) { return; }
    if (this.selectedIndex === 0) {
      this.container.childNodes[0].className = '';
      this.selectedIndex = -1;
      this.el.value = this.currentValue;
      return;
    }
    this.adjustScroll(this.selectedIndex - 1);
  },

  moveDown: function() {
    if (this.selectedIndex === (this.suggestions.length - 1)) { return; }
    this.adjustScroll(this.selectedIndex + 1);
  },

  adjustScroll: function(i) {
    var container = this.container;
    var activeItem = this.activate(i);
    var offsetTop = activeItem.offsetTop;
    var upperBound = container.scrollTop;
    var lowerBound = upperBound + this.options.maxHeight - 25;
    if (offsetTop < upperBound) {
      container.scrollTop = offsetTop;
    } else if (offsetTop > lowerBound) {
      container.scrollTop = offsetTop - this.options.maxHeight + 25;
    }
    this.el.value = this.suggestions[i];
  },

  onSelect: function(i) {
    (this.options.onSelect || Prototype.emptyFunction)(this.suggestions[i], this.data[i]);
  }

};

Event.observe(document, 'dom:loaded', function(){ Autocomplete.isDomLoaded = true; }, false

使用:Event.observe(window, ‘load’, function() {

  function onAutocompleteSelect(value, data){
            //..
    }
    var rand = new Date().getTime();
    var url = 'data.js?r=' + rand;
    new Autocomplete('txtEmployeeNum', { 
        serviceUrl: url, 
        width: 300,  //可选
        onSelect: onAutocompleteSelect, //可选
        showNumber: true //显示条数
        //container: 'ac_container'  //可选
    });
});

<input type="text" name="q" id="txtEmployeeNum" />
<!-- <div id="ac_container"></div>  -->  

data.js 有后台控制,产生json格式数据,如下:

//{query:'z',suggestions:['z','z1','z2','z3']}

//{query:'q',suggestions:['q','q1','q2','q3'],numbers:[99,88,77,66]}

{query:'去',suggestions:['去','去1','去12','去123'],numbers:[99,88,77,66]}

弹出提示层的原型:

Lithuania约88个服务

[转载]PHP书写规范 PHP Coding Standard

mikel阅读(814)

[转载]PHP书写规范 PHP Coding Standard – sink_cup – 博客园.

PHP书写规范
作者:sink <sink.cup@gmail.com>
最后修改:2011-7-7

参考资料:
PHP Manual
http://www.php.net/manual/zh/language.oop5.basic.php
PEAR Coding Standards
http://pear.php.net/manual/en/standards.php
C++ Coding Standard
http://www.possibility.com/Cpp/CppCodingStandard.html
Google C++ Style Guide
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml
Code Conventions for the Java
http://www.oracle.com/technetwork/java/codeconvtoc-136057.html

通用原则:
1、语义化
看到名字,就知道意思。

2、通用前缀
is表示是否、get表示读、set表示写。is后面优先跟形容词,而不是名词,比如是否多语言文字,应使用is_multilingual,而不是is_multilanguage。

3、单数与复数
参考js的函数命名规则:getElementById、getElementsByTagName、getElementsByName。
例如:
取我的多个好友的名字,应使用getFriendsName,而不是getFriendNames或者getFriendName
取一个用户,是getUser
取多个用户,是getUsers

4、冗余后缀
尽量不使用data、list、info后缀。
比如,js的命名就很注意,使用getElementsByTagName而不是getElementsInfoByTagName。
应该使用getFriends或者getFriendsUserId,而不是getFriendsList;应该使用getUser,而不使用getUserInfo或者getUserData。
不过有时候很难避免,比如有2个函数,分别是取用户基本信息,和取用户详细信息。
取用户基本信息:昵称、头像URI,函数名getUserBasic还是getUserBasicInfo?函数名以形容词结尾感觉不合适,待讨论。
取用户详细信息:昵称、头像URI、签名、生日,函数名getUser没问题。

5、含义模糊的类名、文件名、目录名
每当使用common、util、functions、class、object、basic作为文件名时要慎重,由于这些词太通用,发展下去里面东西可 能越来越多,变成垃圾箱。要给这些起一个准确的名字,比如要做字符串处理的类,可以叫StringLib.php,放在lib目录里。

6、lib、plugin与addon的区别
有些类、函数算做lib、plugin还是addon。待讨论。

类名:
大写字母开头,驼峰命名。一般使用名词,比如配置解析类ConfigParser,而不是ParseConfig。
与Java、C++一致。
例如:class UserModel

类的文件名:
与类名相同。这与php autoload有关,为了autoload,类名总要很长,待讨论。
与Java一致。
例如:class UserModel的文件名为UserModel.php

非类文件名:
全小写,下划线分隔,不得使用空格。比如get_user.php。

目录名:
全小写,下划线分隔,不得使用空格。比如model、www。

函数名:
小写字母开头,驼峰命名,例如:function addBlog()。
与Java、C++一致。
函数表示功能,即动作,所以动词优先,例如使用editBlog,而不用blogEdit。
PHP内置函数由于历史原因,有多种风格,do_something,something_do,dosomething,比较新的函数用了doSomething,才与目前主流语言保持一致。
比如:paser_str、json_encode、substr、fetchAll。
历史原因可能无法改变,但我们能保证新的代码是严谨的,不要让自己成为历史原因。

类中的函数:
两个函数中间空一行。如果有时间的话,各个函数按英文字母排序,免得太混乱。
例如:
class BlogModel
{
public function addBlog()
{

}

public function updateBlog()
{

}
}

文件注释:
注释紧跟<?php下一行。注明作者。@version暂不需要写,因为svn提供了版本管理。
格式按照PHPdoc的要求:http://manual.phpdoc.org/HTMLframesConverter/default/phpDocumentor/tutorial_tags.author.pkg.html
<?php
/**
* blog的各种业务:添加、更新
* @author sink
*
*/
class BlogModel
{

}
?>

API注释:
一定要写输入参数,和输出格式。写清楚正确时输出什么,错误时输出什么。
否则别人无法使用。

函数注释:
一定要写输出格式。写清楚正确时输出什么,错误时输出什么。
如果输入参数比较复杂,包含数组,看参数无法一目了然,则要写输入参数的注释。
文档注释与函数之间不能有空行。
如果函数内部步骤比较复杂,需要写“行内注释”。
例如:
/**
* 更新blog
* @param int $id blog_id
* @param array $data array(
“content” => “”, //内容
“tags” => “”, //标签
“update_time” => “”, //更新时间
)
* @return bool
*/
public function updateBlog($id,$data)
{
step1 //第一步:asdf
step2 //第二步:qwer
}

URI:
根据rfc1034国际标准的规定,域名中禁止出现下划线“_”,域名不区分大小写。
比如http://dl_dir.qq.com/是错误域名。
http://example.com与http://EXAMPLE.COM相同。
所以优先在URI中使用全小写,GET的name小写,但是GET的值除外。
比如
http://www.google.com/?hl=zh-CN
http://www.google.com/?hl=zh-cn
URI中非参数的专有名词的缩写是否使用小写,有争议无定论。
比如
http://fedoraproject.org/zh_CN/
http://zh.wikipedia.org/zh-cn/
http://code.google.com/intl/zh-CN/
http://www.microsoft.com/en-us/
语言文字代码是专有名词,ISO规定必须是减号,且建议地区使用大写。
fedora的用法很奇怪,使用了自己制造的zh_CN,而不是zh-CN。而且不建议在URI中使用下划线。
wiki用了小写,google用了大写,微软用了小写。

优先在URI中使用减号“-”,而不是下划线,GET的name除外。
比如
http://example.com/1-2-2
http://example.com/?user_id=123
如果希望用户手动输入URI,则不要区分大小写,且优先使用小写,因为用户输入更方便。
实际情况是:用户一般是手动输入域名,而不手动输入URI,因为URI很长。在这种情况下,URI小写是否有意义,如果使用 http://example.com/?userId=123,变量名就可以使用驼峰$userId = $_GET[‘userId’],就能够和Java、C++保持一致,这样数据库也要驼峰命名。待讨论。

变量:
全小写,下划线分隔,例如:$user_id。
与Java、C++不一致。待讨论。
类的成员变量、函数的形参、类实例化成一个对象,都遵守变量的命名规则。
原因:URI、数据库有小写惯例,从$_GET、$_POST中获得参数入库,所以用小写。
PHP内置变量$_GET、$_POST使用下划线开头,全大写。自定义的变量无论多么重要,都不要使用下划线开头,以免将来与内置变量冲突。
比如:不要使用$_PUT、$_DELETE。

常量:
全大写,下划线分隔。例如:const MEMCACHE_TTL = 600;

PHP短标签:
使用<?php ?>,不使用短标签<? ?>。因为与xml冲突,且不利于部署。

类大括号换行:
可以采用大括号单独占一行,也可以大括号与别的放在一行,有争议无定论,待讨论。
class UserModel
{

}
支持换行者:
http://www.php.net/manual/zh/language.oop5.basic.php
http://pear.php.net/manual/en/standards.classdef.php

函数大括号换行:
有争议无定论,待讨论。
function getUser()
{
}
支持换行者:
http://www.php.net/manual/zh/language.oop5.basic.php
http://pear.php.net/manual/en/standards.funcdef.php

if大括号换行:
有争议无定论,待讨论。
例如:
if(!empty($name))
{

}
或者
if(!empty($name)){

}
支持换行者:
http://www.possibility.com/Cpp/CppCodingStandard.html#brace

支持同行者:
http://www.php.net/manual/zh/language.oop5.basic.php
http://pear.php.net/manual/en/standards.control.php

switch大括号换行:
switch (…)
{
case 1:

break;

default:
}
支持换行者:
http://www.possibility.com/Cpp/CppCodingStandard.html#switch

数组小括号换行:
有争议无定论,待讨论。
$user = array(
“id” => “123”,
“name” => “user1”,
“email” => “a@example.com”,
)
支持同行者:
http://pear.php.net/manual/en/standards.arrays.php

数组内部换行:
2维及以上数组的数组内部换行。

$user = array(
‘id’ => ‘123’,
‘name’ => ‘user1′,
’email’ => ‘a@example.com’,
);
1维数组内部不换行?待讨论。

$users_id = array(’23’,’12’,’24’);

数组最后的逗号:
数组每一行最后要有逗号,这样方便以后添加。不过前端JSON最后不能有逗号,否则有的浏览器不支持,待讨论。
比如
$user = array(
‘id’ => ‘123’,
‘name’ => ‘user1’, //正确
);
$user = array(
‘id’ => ‘123’,
‘name’ => ‘user1’ //错误
);

单引号与双引号:
优先使用单引号,当需要转义时使用双引号。这与JSON不同,JSON全是双引号,待讨论。
比如:
echo ‘name is:’ . $name . ‘.’ . “\n”;
$user = array(
‘id’ => ‘123’,
);

条件判断的大括号:
必须有大括号,即使只有一行。
正确:
if(!empty($name))
{
doSomething();
}
错误:
if(!empty($name))
doSomething();

回车换行:
使用换行LF(\n,0a,Unix风格)。不使用CR+LF(Windows风格)。
参考:http://zh.wikipedia.org/zh-cn/%E6%8F%9B%E8%A1%8C
eclipse——》workspace——》New text file line delimiter——》Other:Unix

编码:
使用UTF-8 no BOM。不得使用Windows记事本进行保存,因为记事本是UTF-8 BOM CR+LF。
eclipse——》workspace——》Text file encoding——》Other:UTF-8

缩进:
使用4个空格进行缩进,也可以采用tab进行缩进。有争议无定论,待讨论。
支持4个空格者:
http://www.oracle.com/technetwork/java/codeconventions-136091.html#262

支持2个空格者:
http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Spaces_vs._Tabs

支持3、4或8个空格者:
http://www.possibility.com/Cpp/CppCodingStandard.html#indent

要保证缩进正确,如果使用4个空格,一定不要出现5个空格或者11个空格。
eclipse——》General——》Editor——》Text Editors——》show whitespace characters
vim ~/.vimrc
set expandtab
set softtabstop=4
set shiftwidth=4

HTTP协议缓存:
文章使用Last Modified表示最后修改时间,不禁止缓存。
header(‘Last Modified:Sat, 30 Oct 2010 13:21:21 GMT’);
需要用户登录的页面,禁止缓存。
header(‘Cache-Control:max-age=0’);
header(‘Cache-Control:private’);

HTTP协议编码与mime:
web输出一定要声明编码与mime。charset与分号之间要有一个空格。小写utf-8还是大写UTF-8,尚未找到文档,待调研。
比如
header(‘Content-Type:application/json; charset=UTF-8’);
header(‘Content-Type:application/xml; charset=UTF-8’);
header(‘Content-Type:application/xhtml+xml; charset=UTF-8’);
header(‘Content-Type:text/plain; charset=UTF-8’);
header(‘Content-Type:text/html; charset=UTF-8’);

专有名词大小写:
在类、函数、文件名、目录名等各种地方,不特殊对待专有名词,不采用全大写。
原因:专有名词难以界定,比如HTML、CSS、CRUD。而且全大写导致与驼峰冲突,比如页面助手类,全大写是HTMLHelper,不如HtmlHelper。
支持不特殊处理:
HTML是专有名词,但mime中就使用Content-Type:text/html,而不是text/HTML。
例子:
采用UserDb.php,而不是UserDB.php。

[转载]判断一个点是否在指定三角形内(1)

mikel阅读(935)

[转载]判断一个点是否在指定三角形内(1) – flyinghearts – 博客园.

问题:判断点P是否在三角形ABC

判断一个点是否在在三角形内,最常用的两种方法:面积法、向量同向法。算法虽然很简单,但要做到高效却不容易,要考虑到二维、三维的区别,还要考虑到坐标是用浮点数还是用整数来表示。

在二维平面上,问题相对简单,一般只需6次乘法计算。但在三维平面时问题要复杂很多,在网上看到的算法,一般都需要30次乘法计算(如果已知点P在平面ABC上,则需21次)。实际上,在三维坐标系下,可以做到增加1次比较,将乘法计算降到13次(如果点P在平面ABC上,则最多只要8次乘法计算)。

最常用的两种方法:面积法和向量同向法本质上是等价的。

向量同向法:若点P在三角形内,则三个向量:ab × apap × acpb × pc平行同向(它们也与向量ab × ac平行同向),由于这三个向量均有可能为0,直接判断它们平行同向相当麻烦,但考虑到ab × ac不可能为0,直接判断“向量:ab × apap × acpb × pc均与ab × ac平行同向”反而更简单。

面积法:当点p在三角形abc内时,4个三角形的面积满足: abc = abp + apc + pbc

对面积的计算,可以通过向量的向量积计算得到: 面积 abc = |ab × ac| / 2

表面上,要计算4个三角形的面积,但根据下面的公式:

ap × ap = 0, pb × pc = (ab – ap) × (ac – ap) = ab × ac – ab × ap – ap × ac

可以少算一次矢量积

公式: |ab × ac| = |ab × ap| + |ap × ac| + |(ab × ac – ab × ap – ap × ac)|

对任意向量abc |a + b + c| = |a| + |b| + |c| <==> 向量abc 平行同向

因而,面积法和向量同向法本质上是等价的。

下面先讨论二维坐标系(每个点X,都看作是原点O到该点X的二维向量OX)。

先定义一个二维向量模板:

template<typename T> class Vec2 {

T x, y;

public:

typedef T value_type;

Vec2(T xx = 0, T yy = 0) : x(xx), y(yy) {};

T cross(const Vec2& v) const { return x * v.y y * v.x;} // 矢量积

Vec2 operator-(const Vec2& v) const { return Vec2(x v.x, y v.y); }

};

如果坐标采用浮点数,考虑到浮点数取绝对值方便(有专门的浮点指令),但彼此间比较大小存在误差,采用面积法比较方便:

typedef Vec2<double> Vd2;

bool is_in_triangle(const Vd2& a, const Vd2& b, const Vd2& c, const Vd2& p)

{

Vd2 ab(b a), ac(c a), ap(p a);

//用矢量积计算面积,下面4个值的绝对值,是对应的三角形的面积的两倍,

double abc = ab.cross(ac);

double abp = ab.cross(ap);

double apc = ap.cross(ac);

double pbc = abc abp apc; //等于pb.cross(pc)

//面积法:4个三角形的面积差 等于 0

double delta = fabs(abc) fabs(abp) fabs(apc) fabs(pbc);

return fabs(delta) < DBL_EPSILON;

}

如果坐标采用整数表示,代码相对麻烦点:

typedef Vec2<int> Vi2;

bool is_in_triangle(const Vi2& a, const Vi2& b, const Vi2& c, const Vi2& p)

{

Vi2 ab(b a), ac(c a), ap(p a);

//用矢量积计算面积,下面4个值的绝对值,是对应的三角形的面积的两倍,

int abc = ab.cross(ac);

int abp = ab.cross(ap);

int apc = ap.cross(ac);

int pbc = abc abp apc; //等于pb.cross(pc)

//方法1: 面积法:4个三角形的面积差 等于 0

return abs(abc) == abs(abp) + abs(apc) + abs(pbc)

//方法2: 矢量同向法: abp apc pbc 均与 abc 同向:

if (abc < 0) { abp = abp; apc = apc; pbc = pbc; }

return (abp >= 0) & (apc >= 0) & (pbc >= 0);

}

方法1:要计算4次绝对值,看似需要4次条件跳转,但主流的编译器,都能采用位运算直接计算绝对值(注意:GCC需要加额外的参数),不需要任何条件跳转。

方法2:比方法1指令少,但多1次条件跳转。

哪种方法效率较高,与编译器生成的具体代码有关。

上面代码中,可采用的两种优化方法:

① 对整数x取绝对值,可以利用位运算:

y = 0 (当x >= 0

= -1 (当x < 0

(编译器可以利用cdqsar等指令直接由x计算出y值)

abs(x)  =  (x xor y) – y

或: = (x + y) xor y

或: = x – (2 * x & y)

对整数abc a >= 0 && b >= 0 && c >= 0 等价于

(a >= 0) & (b >= 0) & (c >= 0) 等价于:

(a | b | c) >= 0

为避免编译器没有进行相关优化,直接手动优化,可得:

inline int chg_sign(int x, int sign) //sign只能取0或-1,函数分别返回x、-x

{

return (x + sign) ^ sign;

//return (x ^ sign) – sign;

}

bool is_in_triangle(const Vi2& a, const Vi2& b, const Vi2& c, const Vi2& p)

{

Vi2 ab(b a), ac(c a), ap(p a);

//用矢量积计算面积,下面4个值的绝对值,是对应的三角形的面积的两倍,

int abc = ab.cross(ac);

int abp = ab.cross(ap);

int apc = ap.cross(ac);

int pbc = abc abp apc; //等于pb.cross(pc)

//方法3: 矢量同向法(优化版)

const int sign = (abc >= 0) 1;

//const int sign = abc >> (sizeof(abc) * CHAR_BIT – 1);

return (chg_sign(abp, sign) | chg_sign(apc, sign) | chg_sign(pbc, sign)) >= 0;

}

作者: flyinghearts
出处: http://www.cnblogs.com/flyinghearts/
本文采用知识共享署名-非商业性使用-相同方式共享 2.5 中国大陆许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

[转载]分享E-WebTemplates国外精美网页模板(FLASH+PSD源文件+HTML)(1~40)

mikel阅读(1248)

[转载]【网页设计】分享E-WebTemplates国外精美网页模板(FLASH+PSD源文件+HTML)(1~40) – 悠游人生 – 博客园.

都是国外E-WebTemplates网站上的网页模板,自己用采集程序把下载链接采集下来的。大概一共159套,样式都很漂亮,每套都含有PSD源文件和生成的HTML。

已经看过的童鞋请54,咔咔。。。

web page template Html website template
下载地址 free web design templates
cool web page design templates
free web page template HTML website template
下载地址 free web design templates
cool web page design templates
free html web template free html website template
下载地址 free web design templates
cool web page design templates
free HTML web template free html website template
下载地址 free web design templates
cool web page design templates
free web template free website design template
下载地址 free web design templates
cool web page design templates
free website template free professional web template
下载地址 free web design templates
cool web page design templates
free professional template free professional web template
下载地址 free web design templates
cool web page design templates
free professional design template HTML website template
下载地址 free web design templates
cool web page design templates
free HTML web page template Html website template
下载地址 free web design templates
cool web page design templates
free professional web template free pro website template
下载地址 free web design templates
cool web page design templates
Professional website templates Professional web template
下载地址 free web design templates
cool web page design templates
free Professional html template free Professional web page template
下载地址 free web design templates
cool web page design templates
free professional web template free professional website template
下载地址 free web design templates
cool web page design templates
free HTML professional template free professional html template
下载地址 free web design templates
cool web page design templates
professional web template free professional web template
下载地址 free web design templates
cool web page design templates
free professional html template free pro web design template
下载地址 free web design templates
cool web page design templates
professional web page template free html web template
下载地址 free web design templates
cool web page design templates
free web page template PRO website template
下载地址 free web design templates
cool web page design templates
free photoshop website template free photoshop web template
下载地址 free web design templates
cool web page design templates
free PSD web page template free photoshop web templates
下载地址 free web design templates
cool web page design templates
free PSD web page template free photoshop website template
下载地址 free web design templates
cool web page design templates
photoshop web page templates free PSD web template
下载地址 free web design templates
cool web page design templates
free photoshop website template free photoshop website template
下载地址 free web design templates
cool web page design templates
free PSD template free web page template
下载地址 free web design templates
cool web page design templates
free PSD template free web page template
下载地址 free web design templates
cool web page design templates
free photoshop template free photoshop website templates
下载地址 free web design templates
cool web page design templates
free PSD website template free Photoshop web page Template
下载地址 free web design templates
cool web page design templates
free photoshop HTML template free HTML photoshop template
下载地址 free web design templates
cool web page design templates
free PSD template free HTML toys template
下载地址 free web design templates
cool web page design templates
free photoshop web template free PSD templates
下载地址 free web design templates
cool web page design templates

[转载]ActionScript3.0中未公开的addFrameScript方法

mikel阅读(1351)

[转载]ActionScript3.0中未公开的addFrameScript方法 – 小枫_AS3.0 – 博客园.

addFrameScript的功能是允许你指定在flash player播放头播放到指定的位置时触发事件.这个方法可以接收四个参数,但常用的只有前两个,关键的意思就是:表示执行到指定帧后调用所指定的函数.

语法:
MovieClip.addFrameScript(frame:uint,notify:Function)

注:frame指的是帧的索引,默认是0,也就是说第一帧是0; notify是指调用函数,也就是说为某一帧添加一个函数;

例:

addFrameScript(0,frame1);

function frame1(){

stop();

trace(“这是第一帧”);

}

以上是一个简单的例子,告诉第一帧,停止,并且trace一句话!

实际上addFrameScript()还可以接收任意多组的参数.如下:

MovieClip.addFrameScript(0,frame1Method, 1,frame2Method, 2,frame3Method);
也就是你说可以同时指定多组触发事件,在1,2,3帧上同时指定触发的动作,如果有不止一个动作事件指定在相同的一个帧上,那么,只有后者的动作会被触发.

真是太帅了,有了这个方法,我们就可以动态的为每个帧随意指定要执行的代码了!

另外,如果要移除动作,也非常方便,如:

MovieClip.addFrameScript(0,null)

MovieClip.addFrameScript(9,output,false,false);

据说,As3.0的addFrameScript()方法并没有公开,但是却非常好用!

[转载].NET MVC3 中扩展一个HtmlHelper方法CheckBoxList

mikel阅读(1083)

[转载].NET MVC3 中扩展一个HtmlHelper方法CheckBoxList – 单程列车 – 博客园.

MVC中有DropDownList方法,挺好用,可是最常用的需求,一组checkboxlist咋没个类似方法呢?郁闷之余,自己做一个吧,直接上代码,呵呵

public static MvcHtmlString CheckBoxList(this HtmlHelper helper, string name, IEnumerable
selectList)
{
return CheckBoxList(helper, name, selectList, new { });
}
public static MvcHtmlString CheckBoxList(this HtmlHelper helper, string name, IEnumerable
selectList, object htmlAttributes)
{

IDictionary HtmlAttributes = HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes);

HashSet set = new HashSet();
List
list = new List
();
string selectedValues = Convert.ToString((selectList as SelectList).SelectedValue);
if (!string.IsNullOrEmpty(selectedValues))
{
if (selectedValues.Contains(","))
{
string[] tempStr = selectedValues.Split(',');
for (int i = 0; i &lt; tempStr.Length; i++)
{
set.Add(tempStr[i]);
}

}
else
{
set.Add(selectedValues);
}
}

foreach (SelectListItem item in selectList)
{
item.Selected = (item.Value != null) ? set.Contains(item.Value) : set.Contains(item.Text);
list.Add(item);
}
selectList = list;

HtmlAttributes.Add("type", "checkbox");
HtmlAttributes.Add("id", name);
HtmlAttributes.Add("name", name);
HtmlAttributes.Add("style", "margin:0 0 0 10px;line-height:30px; vertical-align:-8px;border:none;");

StringBuilder stringBuilder = new StringBuilder();

foreach (SelectListItem selectItem in selectList)
{
IDictionary newHtmlAttributes = HtmlAttributes.DeepCopy();
newHtmlAttributes.Add("value", selectItem.Value);
if(selectItem.Selected)
{
newHtmlAttributes.Add("checked", "checked");
}

TagBuilder tagBuilder = new TagBuilder("input");
tagBuilder.MergeAttributes(newHtmlAttributes);
string inputAllHtml= tagBuilder.ToString(TagRenderMode.SelfClosing);
stringBuilder.AppendFormat(@"<label> {0}  {1}</label>",
inputAllHtml, selectItem.Text);
}
return MvcHtmlString.Create(stringBuilder.ToString());

}
private static IDictionary DeepCopy(this IDictionary ht)
{
Dictionary _ht=new Dictionary();

foreach (var p in ht)
{
_ht.Add(p.Key, p.Value);
}
return _ht;
}

可以直接拿去用

生成的每一个checkbox外部都有一个label,感觉不错,不喜欢可以去掉,还有一个 HtmlAttributes.Add(“style”, “margin:0 0 0 10px;line-height:30px; vertical-align:-8px;border:none;”); 是为了让显示更好看的,如果在你的样式下面位置不好看你可以把这行去掉

[转载]发布新浪微博API SDK,附Demo地址!!

mikel阅读(994)

[转载]发布新浪微博API SDK,附Demo地址!! – DreamTrue – 博客园.

新浪微博没有发布官方的C#版SDK,为了弥补这个遗憾,只好自己动手丰衣足食啦

本次发布的SDK,实现了图片上传功能,支持中文和特殊标点符号,

并且采用了外观模式,隐藏底层实现,开发者直接调用SDK方法即可,现有功能经测试全部通过。

不过由于精力有限,SDK未能涉及到全部的API,如果大家觉得不错,可联系我共同完善。

SDK SVN地址:
https://subversion.assembla.com/svn/easyportal-web-weibo/
SDK Demo地址:
http://demo.zhanghq.net/sinaweibo

获取新浪所有表情截图

上传微博截图:

http://demo.zhanghq.net/sinaweibo/Message/Upload

上传微博前请先连接到新浪

SDK里的演示站点,需要安装MVC3

PS:新浪API的开发过程比较坑爹,很多API文档跟实际差别比较大,不少状况只能通过不停调试解决,弄了将近半个月,总算可以在项目中用了,:)

[转载]JavaScript上传头像截图,仿flash

mikel阅读(1413)

[转载]【原创】JS上传头像,仿flash – loogn – 博客园.

flash上传头像多不胜举了,可JS仿flash的见的不多,也是工作需要,就试着写写看!不料,设想还真的可行

先看两个效果图(在FF下):

1,选择上传图片区域

2,上传后图片到img标签

下面是我整个的思路:

一、用ajaxFileUpload.js异步上传图片。这个不用多说了,挺简单的东西。

二、div拖拽用JQuery-ui。这个也不多说了。

三、上传过图片的处理。

编辑容器是300*300,如果图片不在300*300之内,这里会生成一个maxHeight=300,maxWidth=300的缩略图,

用于设置编辑容器的背景,如果原文件为123.jpg,这里生成的文件为123.jpg.view.jpg,当然,文件123.jpg.view.jpg可能不会生成!

返回数据为{ result:” + result + “,size:” + size + “,msg:\”” + msg + “\”,w:” + ww + “,h:” + hh + “}”。

result 是1或0,表是否上传成功,

size 是缩放比率,默认为1,有缩略图时为缩小的倍数,如果缩略到原来的3/4,size为0.75

msg 如果result=1,msg是文件地址,如果result=0,msg是错误信息

w,h是原图的宽高,如果有缩略图,则为缩略图的宽高。

四、选择图片区域

头像目标大小为175*175,编辑时在右边显示。目标头像要随选区的移动而移动,这个可以定位背景要实现,但它还要随选区的大小变化而变化,这个用背景恐怕是难以实现了吧!

反正我是不知道怎么做,所以这里选择用真正的图片,可图片从哪来呢?从服务端来!想必图片验证码大家都熟悉,这里的真正图片就是这样得来的。在选区drag和resize后(当然也可

在进行时请求,效果更动态,不过嘿嘿…),即 stop时要向服务器请求显示的图片,请求数据要包含size,即上面得到的0.75,还有选区的位置和大小。

到服务器后你会发现选区的位置(x,y)和大小(w,h)是针对缩略图的,这时 size就有用了,由size缩略,由size还原,真是解玲还需系玲人啊,把x,y,w,h都除以size,得到的位置和大小便对应到原图上了。于是 乎,剪切吧!返回吧!好,剪切,返@#!,晕,这里还不能返回呢!因为剪切过后你也不知道大小是不是符合

我们的目标大小(175*175)啊! 所以这里不能急着返回给用户看,还要做最后的包装,把大的压缩,把小的放大,统统成175*175达标后才可返回。于是,

页面上就可以看到我们选区的头像了~

五、确定保存

这里逻辑就简单多了,选择图片区域时我们生成了图片输出到页面,这里我们可以用同样的方法生成图片,保存就OK了,这里还有一个清理操作,

最终的123.jpg是没用的,123.jpg.view.jpg也是没用的,有用的是我们借助123.jpg.view.jpg从132.jpg中抽离出来的456.jpg!

六、整个功能放在一个html里,哪里用,就load到哪里!

js:
$(function() {
$("#divUp").load("uploadAvatar.htm?n=" + Math.random());
})
function OnAvatarUploaded(file) {
$("#img").attr("src", file + "?n=" + Math.random()); //防止缓存
}

html:
<div style="height: 500px; clear: both; margin: 80px auto; width: 800px;">
<div style="width: 175px; height: 175px; float: left; border: 1px solid #cccccc;"><img id="img" style="width: 175px; height: 175px;" alt="" /></div>
<div id="divUp" style="width: 520px; float: left; margin-left: 20px;"></div>
</div>

*******************

总体思路及步骤到这就完了。

功能没有flash的强大,但一般的上传头像也可以满足了,想生成头像缩略图也很简单,下面会附源码,看一下就知道在哪加了。

handler里代码有点乱,没有整理,不好意思啦!

下载